home *** CD-ROM | disk | FTP | other *** search
Text File | 1991-09-15 | 324.6 KB | 6,618 lines |
- % SUBJECT: Professional GEM, 09.09.91
- %
- % These are all the sections. Appendix A-C are include files
- % (app_a.tex, app_b.tex, app_c.tex).
- %
- % Layout changes:
- %
- % all sans-serif emphasizes removed 09.09.1991 - mo
-
- % 4 Ollie's DIN-5 zoom...
- % \documentstyle[12pt,a4,twoside,titlepage]{article}
- %
-
- \documentstyle[a4wide,twoside,titlepage]{article}
-
- \pagestyle{headings}
-
- %\textwidth15.6cm\textheight23cm
-
- \sloppy
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \begin{document}
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- \title{Professional GEM}
- \author{by Tim Oren}
-
- \maketitle
- \newpage
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \pagenumbering{roman}
- \tableofcontents\clearpage
- \newpage
- \pagenumbering{arabic}
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \section{Introduction}
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Hello, world!}
-
- For those whom I have not met in person or electronically, an
- introduction is in order.
-
- I am a former member of the GEM programming team at Digital Research,
- Inc., where I designed and implemented the GEM Resource Construction
- Set and other parts of the GEM Programmer's Toolkit. I have since
- left DRI to become the user interface designer for Activenture, a
- startup company which is developing CD-ROM technology for use with
- the Atari ST and other systems.
-
- The purpose of Professional GEM is to pass along some of the
- information and tricks I have accumulated about GEM, and explore some
- of the user interface techniques which a powerful graphics processor
- such as the ST makes possible.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Ground Rules}
-
- I am going to assume that you have both a working knowledge of the C
- programming language and a copy of the ST Programmer's Toolkit with
- documentation (available from Atari). If you lack either, don't
- panic. You can read the columns to get the flavor of programming the
- ST, and come back for a more serious visit later on.
-
- For now, I will be using code samples that will run with the
- Atari-supplied C compiler, also known as DR C-68K, or Alcyon C. I
- will be using the portability macros supplied with the Toolkit, so
- that the code will also be transferable to other GEM systems.
-
- Both of these items are subject to change, depending on reader
- feedback and the availability of better products.
-
- If you do not have a copy of the source to the {\tt DOODLE.C} GEM example
- program, you should consider downloading a copy from SIG*ATARI.
- Although it is poorly documented, it shows real-life examples of many
- of the techniques I will discuss.
-
- Getting started with a windowed graphics system seems to be like
- getting into an ice-cold swimming pool: it's best done all at once.
-
- Anyone who has looked at {\em Inside Macintosh} has probably noticed that
- you have to have read most of it to understand any of it. GEM isn't
- really much different. You have all the reference guides in your
- hand, but nothing to show how it all works together.
-
- I am hoping to help this situation by leading a series of short tours
- through the GEM jungle. Each time we'll go out with a particular
- goal in mind and follow the path that leads there. We'll look at the
- pitfalls and strange bugs that lurk for the unwary, and show off a
- few tricks to amaze the natives. The first trip leaves immediately;
- our mission is to get a window onto the ST screen, with all of its
- parts properly initialized.
- \newpage
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \section{Windows}
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{We do windows}
-
- One of the most important services which a graphics interface system
- provides for the user and programmer is window management.
-
- Windows allow the user to perform more than one activity on the same
- screen, to freely reallocate areas of the screen for each task, and
- even to pile the information up like pages of paper to make more
- room. The price for this increased freedom is (as usual) paid by
- you, the programmer, who must master a more complex method of
- interacting with the ``outside world''.
-
- The windowing routines provided by ST GEM are the most comprehensive
- yet available in a low-cost microcomputer. This article is a guide
- to using these services in an effective manner.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{In the beginning}
-
- In GEM, creating a window and displaying it are two different
- functions. The creation function is called {\tt wind\_create}, and its
- calling sequence is:
-
- \begin{verbatim}
- handle = wind_create(parts, xfull, yfull, wfull, hfull);
- \end{verbatim}
-
- This function asks GEM to reserve space in its memory for a new
- window description, and to return a code or {\em handle}\/ which you can
- use to refer to the window in the future. Valid window handles are
- positive integers; they are not memory pointers.
-
- GEM can run out of window handles. If it does so, the value returned
- is negative. Your code should always check for this situation and
- ask the program's user to close some windows and retry if possible.
- Handle zero is special. It refers to the {\em desktop}, which is
- predefined as light green (or gray) on the ST. Window zero is always
- present and may be used, but never deleted, by the programmer.
-
- The {\tt xfull}, {\tt yfull}, {\tt wfull}, and {\tt hfull}\/ parameters
- are integers which determine the maximum size of the window.
- {\tt xfull}\/ and {\tt yfull}\/ define the upper left corner of the window,
- and {\tt wfull}\/ and {\tt hfull}\/ specify its width and height.
- (Note that all of the window coordinates which we use are in pixel units.)
-
- GEM saves these values so that the program can get them later when
- processing FULL requests. Usually the best maximum size for a
- window is the entire desktop area, excepting the menu bar. You can
- find this by asking {\tt wind\_get} for the working area of the desktop
- (handle zero, remember):
-
- \begin{verbatim}
- wind_get(0, WF_WXYWH, &xfull, &yfull, &wfull, &hfull);
- \end{verbatim}
-
- Note that {\tt WF\_WXYWH}, and all of the other mnemonics used in this
- article, are defined in the {\tt GEMDEFS.H} file in the ST Toolkit.
-
- The parts parameter of {\tt wind\_create} defines what features will be
- included in the window when it is drawn. It is a word of single bit
- flags which indicate the presence/absence of each feature. To
- request multiple features, the flags are {\em or-ed}\/ together. The flags'
- mnemonics and meanings are:
-
- \begin{description}
-
- \item[NAME] A one character high title bar at the top of the window.
-
- \item[INFO] A second character line below the NAME.
-
- \item[MOVER] This lets the user move the window around by {\em dragging}\/
- in the NAME area. NAME also needs to be defined.
-
- \item[CLOSER] A square box at the upper left. Clicking this control
- point asks that the window be removed from the screen.
-
- \item[FULLER] A diamond at upper right. Clicking this control point
- requests that the window grow to its maximum size, or shrink back
- down if it is already big.
-
- \item[SIZER] An arrow at bottom right. Dragging the SIZER lets the
- user choose a new size for the window.
-
- \item[VSLIDE] defines a right-hand scroll box and bar for the window.
- By dragging the scroll bar, the user requests that the window's
- {\em viewport}\/ into the information be moved. Clicking on the gray box
- above the bar requests that the window be moved up one {\em page}.
- Clicking below the bar requests a down page movement. You have to
- define what constitutes a page or line in the context of your
- application.
-
- \item[UPARROW] An arrow above the right scroll bar. Clicking here
- requests that the window be moved up one {\em line\/}. Sliders and arrows
- almost always appear together.
-
- \item[DNARROW] An arrow below the right scroll bar. Requests that window
- be moved down a line.
-
- \item[HSLIDE] These features are the horizontal equivalent of the
- VSLIDE above. They appear at the bottom of the window. Arrows
- LFARROW and RTARROW usually indicate {\em character\/} sized movement left
- and right.
- {\em Page}\/ sized movement has to be defined by each application.
- RTARROW and LFARROW are the horizontal equivalents of the
- vertical slider components.
-
- \end{description}
-
- It is important to understand the correspondence between window
- features and event messages which are sent to the application by the
- GEM window manager. If a feature is not included in a window's
- creation, the user cannot perform the corresponding action, and your
- application will never receive the matching message type. For
- example, a window without a {\tt MOVER}\/ may not be dragged by the user,
- and your app will never get a {\tt WM\_MOVED}\/ message for that window.
-
- Another important principle is that the application itself is
- responsible for implementing the user's window action request when a
- message is received. This gives the application a chance to accept,
- modify, or reject the user's request.
-
- As an example, if a {\tt WM\_MOVED}\/ message is received, it indicates that
- the user has dragged the window. You might want to byte or word
- align the requested position before proceeding to move the window.
- The {\tt wind\_set} calls used to perform the actual movements will be
- described in a minute$\ldots$
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsubsection{Open, Sesame!}
-
- The {\tt wind\_open} call is used to actually make the window
- appear on the screen. It animates a {\em zoom box}\/ on the screen and
- then draws in the window's frame. The calling sequence is:
-
- \begin{verbatim}
- wind_open(handle, x, y, w, h);
- \end{verbatim}
-
- The handle is the one returned by {\tt wind\_create}.
- Parameters {\tt x}, {\tt y}, {\tt w},
- and {\tt h}\/ define the initial location and size of the window. Note that
- these measurements INCLUDE all of the window frame parts which you
- have requested. To find out the size of the area inside the frame,
- you can use
-
- \begin{verbatim}
- wind_get(handle, WF_WXYWH, &inner_x, &inner_y, &inner_w, &inner_h);
- \end{verbatim}
-
- Whatever size you choose for the window display, it cannot be any
- larger than the full size declared in {\tt wind\_create}.
-
- Here is a good place to take note of a useful utility for calculating
- window sizes. If you know the {\em parts list}\/ for a window, and its
- inner or outer size, you can find the other size with the {\tt wind\_calc}
- call:
-
- \begin{verbatim}
- wind_calc(parts, kind, input_x, input_y, input_w, input_h,
- &output_x, &output_y, &output_w, &output_h);
- \end{verbatim}
-
- Kind is set to zero if the input coordinates are the inner area, and
- you are calculating the outer size. Kind is one if the inputs are
- the outer size and you want the equivalent inner size. Parts are
- just the same as in {\tt wind\_create}.
-
- There is one common bug in using {\tt wind\_open}. If the {\tt NAME}\/
- feature is
- specified, then the window title must be initialized BEFORE opening
- the window:
-
- \begin{verbatim}
- wind_set(handle, WF_NAME, ADDR(title), 0, 0);
- \end{verbatim}
-
- If you don't do this, you may get gibberish in the NAME area or the
- system may crash. Likewise, if you have specified the INFO feature,
- you must make a {\tt wind\_set} call for {\tt WF\_INFO}\/ before opening the
- window.
-
- Strings whose addresses are passed in the {\tt WF\_NAME}\/ and {\tt WF\_INFO}\/
- {\tt wind\_set} calls must be allocated in a static data area. Since the
- AES remembers the addresses (not the characters), a disaster may result
- if the storage has been reused when the window manager next attempts to
- draw the window title area.
-
- Note that {\tt ADDR()} specifies the 32-bit address of title. This
- expression is portable to other (Intel-based) GEM systems. If you
- don't care about portability, then \verb"&title[0]", or just \verb"title"
- alone will work fine on the ST.
-
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsubsection{Cleaning Up}
- When you are done with a window, it should be closed
- and deleted. The call
-
- \begin{verbatim}
- wind_close(handle);
- \end{verbatim}
-
- takes the window off the screen, redraws the desktop underneath it,
- and animates a {\em zoom down}\/ box. It doesn't delete the window's
- definition, so you can reopen it later.
-
- Deleting the window removes its definition from the system, and makes
- that handle available for reuse. Always close windows before
- deleting, or you may leave a {\em dead}\/ picture on the screen. Also be
- sure to delete all of your windows before ending the program, or
- your app may {\em eat}\/ window handles. The syntax for deleting a window
- is:
-
- \begin{verbatim}
- wind_delete(handle);
- \end{verbatim}
-
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsubsection{Those Fat Sliders}
-
- One of ST GEM's unique features is the
- proportional slider bar. Unlike other windowing systems, this type
- of bar gives visual feedback on the fraction of a document which is
- being viewed, as well as the position within the document. The
- catch, of course, is that you have two variables to maintain for each
- scroll bar: size and position.
-
- Both bar size and position range from 1~to~1000. A bar size of 1000
- fills the slide box, and a value of one gets the minimum bar size.
- To compute the proper size, you can use the formula:
-
- \begin{verbatim}
- size = min(1000, 1000 * seen_doc / total_doc)
- \end{verbatim}
-
- {\tt seen\_doc}\/ and {\tt total\_doc}\/ are the visible and total size of
- the document
- respectively, in whatever units are appropriate. As an example, if
- your window could show 20 lines of a 100 line text file, you should
- set a slider size of 200. Since the window might be bigger than the
- total document at some points, you need the maximum function. If
- the document size is zero, force the slider size to 1000. (Note: You
- will probably need to do the computation above with 32-bit
- arithmetic to avoid overflow problems.)
-
- Once you have computed the size, use the {\tt wind\_set} function to
- configure the scroll bar:
-
- \begin{verbatim}
- wind_set(handle, WF_VSLSIZE, size, 0, 0, 0);
- \end{verbatim}
-
- This call sets the vertical (right hand) scroll bar.
- Use {\tt WF\_HSLSIZE}\/
- for the horizontal scroller. All of these examples are done for the
- vertical dimension, but the principles are identical in the other
- direction.
-
- Bar positioning is a little tougher. The most confusing aspect is
- that the 1--1000 range does not set an absolute position of the bar
- within the scroll box. Instead, it positions the TOP of the bar
- within its possible range of variation.
-
- Let's look at our text file example again to make this clearer. If
- there are always 20 lines of a 100 line file visible, then the top of
- the window must be always be somewhere between line 1 and line 81.
- This 80 line range is the actual freedom of movement of the window.
- So, if the window were actually positioned with its top at line 61,
- it would be at the three-quarter position within the range, and we
- should set a scroll bar position of 750. The actual formula for
- computing the position is:
-
- \begin{verbatim}
- pos = 1000 * (top_wind - top_doc) / (total_doc - seen_doc)
- \end{verbatim}
-
- {\tt top\_wind}\/ and {\tt top\_doc}\/ are the top line in the current window and the
- whole document, respectively. Obviously, if {\tt seen\_doc} is greater or
- equal to {\tt total\_doc}, you need to force a zero value for pos. This
- calculation may seem rather convoluted the first time through, but is
- easy once you have done it. When you have computed the position,
- \verb"wind_set" configures the scroll bar:
-
- \begin{verbatim}
- wind_set(handle, WF_VSLIDE, pos, 0, 0, 0);
- \end{verbatim}
-
- {\tt WF\_HSLIDE}\/ is the equivalent for horizontal scrolling.
-
- It is a good practice to avoid setting the slider size or position if
- they are already at the value which you need. This avoids an
- annoying redraw flash on the screen when it is not necessary. You
- can check on the current value of a slider parameter with {\tt wind\_get}:
-
- \begin{verbatim}
- wind_get(handle, WF_VSLIDE, &curr_value, &foo, &foo, &foo);
- \end{verbatim}
-
- {\tt foo}\/ is a dummy variable which needs to be there, but is not used.
- Substitute {\tt WF\_VSLIDE}\/ with whatever parameter you are checking.
-
- One philosophical note on the use of sliders: It is probably best to
- avoid the use of both sliders at once unless it is clearly
- appropriate to the type of data which is being viewed.
-
- Since Write and Paint programs make use of the sheet-of-paper
- metaphor, moving the window around in both dimensions is reasonable.
- However, if the data is more randomly organized, such as a tableau of
- icons, then it is probably better to only scroll in the vertical
- dimension and {\em reshuffle}\/ if the window's width is changed. Then the
- user only needs to manipulate one control to find information which
- is off-screen. Anyone who has had trouble finding a file or folder
- within a Desktop window will recognize this problem.
-
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Excelsior!}
-
- The file for this chapter is {\tt GEMCL02.C}. All references to non-GEM
- routines in this column refer to this file. Please note that these
- files will not contain entire programs. Instead, they consist of
- small pieces of utility code which you may use and modify in your
- own programs.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Redrawing windows}
-
- One of the most misunderstood parts of GEM is the correct method for
- drawing within a window. Most requests for redrawing are generated
- by the GEM system, and arrive as messages (read with {\tt evnt\_multi})
- which contain the handle of the window, and the screen rectangle
- which is {\em dirty}\/ and needs to be redrawn.
-
- Screen areas may become dirty as a result of windows being closed,
- sized down, or moved, thus {\em exposing}\/ an area underneath. The
- completion of a dialog, or closing of a desk accessory may also free
- up a screen area which needs to be redrawn. When GEM detects the
- presence of a dirty rectangle, it checks its list of open windows,
- and sends the application a redraw message for each of its windows
- which intersects the dirty area.
-
- GEM does not {\em clip}\/ the rectangle which it sends to the application;
- that is, the rectangle may not lie entirely within the portion of
- the window which is exposed on the screen. It is the job of the
- application to determine in what portion of the rectangle it may
- safely draw. This is done by examining the {\em rectangle list}\/
- associated with the window.
-
- A rectangle list is maintained by GEM for each active window. It
- contains the portions of the window's interior which are exposed,
- i.e., topmost, on the screen and within which the app may draw.
-
- Let's consider an example to make this clear. Suppose an app has
- opened two windows, and there are no desk accessory windows open. The
- window which is topmost will always have only one rectangle in its
- list. If the two are separate on the screen, then the second window
- will also have one rectangle. If they overlap, then the top window
- will "break" the rectangle of the bottom one. If the overlap is at
- a corner, two rectangles will be generated for the bottom window. If
- the overlap is on a side only, then three rectangles are required to
- cover the exposed portion of the bottom window. Finally, if the
- first window is entirely within the second, it requires four
- rectangles in the list to tile the second window.
-
- Try working out a few rectangle examples with pencil and paper to
- get the feel of it. You will see that the possible combinations with
- more than two windows are enormous. This, by the way, is the reason
- that GEM does not send one message for each rectangle on the list:
- With multiple windows, the number of messages generated would
- quickly fill up the application's message queue.
-
- Finally, note that every app MUST use this method, even if it only
- uses a single window, because there may be desk accessories with
- their own windows in the system at the same time. If you do not use
- the rectangle lists, you may overwrite an accessory's window.
-
- One more note on rectangles: although AES intersects the rectangles
- with the window work area, parts of a rectangle may be off screen because
- the window is partially off screen. Redrawing may then lead to interesting
- results. Intersecting a rectangle with the screen
- {\tt GRECT} before redrawing will solve this problem.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Into the bits}
-
- First, we should note that the message type for a redraw request is
- {\tt WM\_REDRAW}, which is stored in \verb"msg[0]", the first location of the
- message returned by {\tt evnt\_multi}. The window handle is stored in
- \verb"msg[3]". These locations are the same for all of the message types
- being discuss. The rectangle which needs to be redrawn is stored in
- \verb"msg[4]" through \verb"msg[7]".
-
- Now let's examine the sample redraw code in more detail. The redraw
- loop is bracketed with mouse off and mouse on calls. If you forget
- to do this, the mouse pointer will be over-written if it is within
- the window and the next movement of the mouse will leave a
- rectangular blotch on the screen as a piece of the {\em old}\/ screen is
- incorrectly restored.
-
- The other necessary step is to set the window update flag. This
- prevents the menu manager from dropping a menu on top of the screen
- portion being redrawn. You must release this flag at the end of the
- redraw, or the you will be unable to use any menus afterwards.
-
- The window rectangles are retrieved using a get-first, get-next
- scheme which will be familiar if you have used the GEM DOS or PC-DOS
- wildcard file calls. The end of the rectangle list has been reached
- when both the width and height returned are zero. Since some part of
- a window might be off-screen (unless you have clamped its position -
- see below), the retrieved rectangle is intersected with the desktop's
- area, and then with the screen area for which a redraw was
- requested.
-
- Now you have the particular area of the screen in which it is legal
- to draw. Unless there is only one window in your application, you
- will have to test the handle in the redraw request to figure out what
- to put in the rectangle.
-
- Depending on the app, you may be drawing an AES object tree, or
- executing VDI calls, or some combination of the two. In the AES
- case, the computed rectangle is used to specify the bounds of the
- {\tt objc\_draw}. For VDI work, the rectangle is used to set the clipping
- area before executing the VDI calls.
-
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{A small confession}
-
- At the beginning of this discussion, I deliberately omitted one
- class of redraws: those initiated by the application itself.
-
- In some cases a part of the screen must be redrawn immediately to
- give feedback to the user following a keystroke, button, or mouse
- action. In these cases, the application could call {\tt do\_redraw}
- directly, without waiting for a message.
-
- The only time you can bypass {\tt do\_redraw}, and draw without walking the
- rectangle list, is when you can be sure that the target window is on
- top, and that the figure being drawn is entirely contained within it.
-
- In many cases, however, an application initiated redraw happens
- because of a computed change, for instance, a spreadsheet update, and
- its timing is not crucial. In this instance, you may wish to have
- the app send ITSELF a redraw request.
-
- The main advantage of this approach is that the AES is smart enough
- to see if there is already a redraw request for the same window in
- the queue, and, if so, to merge the requests by doing a union of
- their rectangles. In this fashion, the {\em blinky}\/ appearance of
- multiple redraws is avoided, without the need to include logic for
- merging redraws within the program.
-
- A utility routine for sending the {\em self-redraw}\/ is included in
- {\tt GEMCL02.C}.
-
- A number of developers reported that they were unable to get
- the self-redraw technique to work.
- This is usually due to a bug in the {\tt appl\_init} binding in Alcyon C.
- The value returned from the function, which would normally be
- assigned to \verb"gl_apid", is coming back as garbage. To work around
- the problem, declare
-
- \verb"EXTERN WORD gl_apid;"
-
- \noindent in your program and {\em do not}\/ assign the value from
- {\tt appl\_init}. The binding WILL make the assignment.
-
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Window Control Requests}
-
- An application is notified by the AES, via the message system, when
- the user manipulates one of the window control points. Remember that
- you must have specified each control point when the window was
- created, or will not receive the associated control message.
-
- The most important thing to understand about window control is that
- the change which the user requested does not take place until the
- application forwards it to the AES. While this makes for a little
- extra work, it gives the program a chance to intervene and validate
- or modify the request to suit.
-
- A second thing to keep in mind is that not all window updates cause a
- redraw request to be generated for the window, because the AES
- attempts to save time with raster moves on the screen.
-
- Now let's look at each window control request in detail. The
- message code for a window move is {\tt WM\_MOVED}. If you are willing to
- accept any such request, just do:
-
- \begin{verbatim}
- wind_set(wh, WF_CXYWH, msg[4], msg[5], msg[6], msg[7]);
- \end{verbatim}
-
- (Remember that {\tt wh}, the window handle, is always in \verb"msg[3]").
- \vskip.2cm
-
- The AES will not request a redraw of the window following this call,
- unless the window is being moved from a location which is partially
- {\em off-screen}. Instead, it will do a {\em blit}\/ (raster copy) of the
- window and its contents to the new location without intervention by
- the app.
-
- There are two constraints which you may often wish to apply to the
- user's move request. The first is to force the new location to lie
- entirely within the desktop, rather than partially off-screen. You
- can do this with the {\tt rc\_constrain} utility by executing:
-
- \begin{verbatim}
- rc_constrain(&full, &msg[4]);
- \end{verbatim}
-
- \noindent before making the {\tt wind\_set} call. ({\tt full}\/ is assumed to contain
- the desktop dimensions.)
-
- The second common constraint is to {\em snap}\/ the x-dimension location of
- the new location to a word boundary. This operation will speed up
- GEM's {\em blit}\/ because no shifting or masking will need to be done
- when moving the window. To perform this operation, use {\tt align()}
- before the {\tt wind\_set} call:
-
- \begin{verbatim}
- msg[4] = align(msg[4], 16);
- \end{verbatim}
-
- Calling {\tt wind\_set} with an x-coordinate of -1
- will cause the left border of the window not to be displayed, which is
- especially convenient when snapping to word boundaries.
-
- The message code for a window size request is {\tt WM\_SIZED}. Again, if
- you are willing to accept any request, you can just {\em turn it around}\/
- with the same {\tt wind\_set} call as given for {\tt WM\_MOVED}.
-
- Actually, GEM enforces a couple of constraints on sizing. First, the
- window may not be sized off screen. Second, there is a minimum
- window size which is dependent on the window components specified
- when it was created. This prevents features like scroll arrows from
- being squeezed into oblivion.
-
- The most common application constraint on sizing is to snap the size
- to horizontal words (as above) and/or vertical character lines. In
- the latter case, the vertical dimension of the output font is used
- with {\tt align()}.
-
- Also, be aware that the size message which you receive specifies the
- EXTERNAL dimensions of the window. To assure an {\em even}\/ size for the
- INTERNAL dimensions, you must make a {\tt wind\_calc} call to compute them,
- use {\tt align()} on the computed values, back out the corresponding
- external dimensions with the reverse {\tt wind\_calc}, and then make the
- {\tt wind\_set} call with this set of values.
-
- A window resize will only cause a redraw request for the window if
- the size is being increased in at least one dimension. This is
- satisfactory for most applications, but if you must {\em reshuffle}\/ the
- window after a size-down, you should send yourself a redraw (as
- described above) after you make the {\tt wind\_set} call. This will
- guarantee that the display is updated correctly. Also note that the
- sizing or movement of one window may cause redraw requests to be
- generated for other windows which are uncovered by the change.
-
- The window full request, with code {\tt WM\_FULLED}, is actually a toggle.
- If the window is already at its full size (as specified in the
- {\tt wind\_create}), then this is a request to shrink to its previous size.
- If the window is currently small, then the request is to grow to full
- size.
-
- Since the AES records the current, previous, and maximum window size,
- you can use {\tt wind\_get} calls to determine which situation pertains.
- The {\tt hndl\_full} utility in {\tt GEMCL2.C} (modified from Doodle), shows
- how to do this.
-
- The {\em zoom box}\/ effects when changing size are optional, and can be
- removed to speed things up. Again, if the window's size is
- decreasing, no redraw is generated, so you must send yourself one if
- necessary. You should not have to perform any constraint or {\em snap}\/
- operations here, since (presumably) the full and previous sizes have
- had these checks applied to them already.
-
- The {\tt WM\_CLOSED} message is received when the close box is clicked. What
- action you perform depends on the application. If you want to remove
- the window, use {\tt wind\_close} as described earlier. In many
- applications, however, the close message may indicate that a file is
- to be saved, or a directory or editing level is to be closed. In
- these cases, the message is used to trigger this action before or
- instead of the {\tt wind\_close}. (Folders on the Desktop are an example of
- this situation.)
-
- The {\tt WM\_TOPPED}\/ message indicates that the AES wants to bring the
- indicated window to the {\em top}\/ and make it active. This happens if
- the user clicks within a window which is not on top, or if the
- currently topped window is closed by its application or desk
- accessory. Normally, the application should respond to this message
- with:
-
- \begin{verbatim}
- wind_set(wh, WF_TOP, 0, 0);
- \end{verbatim}
-
- \noindent and allow the process to complete.
-
- In a few instances, a window may be used in an output only mode, such
- as a status display, with at least one other window present for
- input. In this case, a {\tt WM\_TOPPED}\/ message for the status window may
- be ignored. In all other cases, you must handle the {\tt WM\_TOPPED}\/
- message even if your application has only one window: Invocation of a
- desk accessory could always place another window on top. If you fail
- to do so, subsequent redraws for your window may not be processed
- correctly.
-
-
- % bis hier wurden Funktionen in sans serif gesetzt
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Window Slider Messages}
-
- If you specify all of the slider bar parts for your window, you may
- receive up to five different message types for each of the two sets
- of sliders. To simplify things a little, I will discuss everything
- in terms of the vertical (right hand side) sliders. If you are also
- using the horizontal sliders, the same techniques will work, just use
- the alternate mnemonics.
-
- The \verb"WM_VSLID" message indicates that the user has dragged the slider
- bar within its box, indicating a new relative position within the
- document. Along with the window handle, this message includes the
- relative position between 1 and 1000 in \verb"msg[4]".
-
- Recall from the previous discussion that this interval corresponds
- to the "freedom of movement" of the slider. If you want to accept the
- user's request, just make the call:
-
- \begin{verbatim}
- wind_set(wh, WF_VSLIDE, msg[4], 0, 0, 0);
- \end{verbatim}
-
- (Corresponding horizontal mnemonics are \verb"WM_HSLID" and \verb"WF_HSLIDE").
-
- Note that this \verb"wind_set" call will not cause a redraw message to be
- sent. You must update the display to reflect the new scrolled
- position, either by executing a redraw directly, or by sending
- yourself a message.
-
- If the document within the window has some structure, you may not
- wish to accept all slider positions. Instead you may want to force
- the scroll position to the nearest text line (for instance). Using
- terms defined in the last column, you may convert the slider position
- to "document units" with:
-
- \begin{verbatim}
- top_wind = msg[4] * (total_doc - seen_doc) / 1000 + top_doc
- \end{verbatim}
-
- (This will probably require 32-bit arithmetic).
-
- After rounding off or otherwise modifying the request, convert it
- back to slider units and make the \verb"WF_VSLIDE" request.
-
- The other four slider requests all share one message code:
- \verb"WM_ARROWED". They are distinguished by sub-codes stored in \verb"msg[4]":
- \verb"WA_UPPAGE", \verb"WA_DNPAGE", \verb"WA_UPLINE", and \verb"WA_DNLINE". These are produced
- by clicking above and below the slider, and on the up and down
- arrows, respectively. (I have no idea why sub-codes were used in
- this one instance.) The corresponding horizontal slider codes are:
- \verb"WA_LFPAGE", \verb"WA_RTPAGE", \verb"WA_LFLINE", and \verb"WA_RTLINE".
-
- What interpretation you give to these requests will depend on the
- application. In the most common instance, text documents, the
- customary method is to change the top of window position (top\_wind)
- by one line for a \verb"WA_UPLINE" or \verb"WA_DNLINE", and by {\tt seen\_doc} (the
- number of lines in the window) for a \verb"WA_UPPAGE" or \verb"WA_DNPAGE".
-
- After making the change, compute a new slider position, and make the
- \verb"wind_set" call as given above. If the document's length is not an
- even multiple of "lines" or "pages" you will have to be careful that
- incrementing or decrementing {\tt top\_wind} does not exceed its range of
- freedom:
- {\tt top\_doc} to ({\tt top\_doc} + {\tt total\_doc} - {\tt seen\_doc}).
-
- If you have such an odd size document, you will also have to make a
- decision on whether to violate the line positioning rule so that the
- slider may be put at its bottom-most position, or to follow the rule
- but make it impossible to get the slider to the extreme of its range.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{A common bug}
-
- It is easy to forget that user clicks are not the only things that
- affect slider position. If the window size changes as a result of a
- \verb"WM_SIZED" or \verb"WM_FULLED" message, the app must also update its sliders
- (if they are present). This is a good reason to keep the top of
- window information in "document units".
-
- You can just redo the position calculation with the new {\tt seen\_doc}\/
- value, and call \verb"wind_set". Also remember that changing the size of
- the underlying document (adding or deleting a bottom line, for
- instance) must also cause the sliders to be adjusted.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Dept.~of Dirty Tricks}
-
- There are two remaining window calls which are useful to advanced
- programmers. They require techniques which I have not yet discussed,
- so you may need to file them for future reference.
-
- The AES maintains a quarter-screen sized buffer which is used to save
- the area under alerts and menu drop-downs. It is occasionally useful
- for the application to gain access to this buffer for its own use in
- saving screen areas with raster copies. To do so, use:
-
- \begin{verbatim}
- wind_get(0, WF_SCREEN, &loaddr, &hiaddr, &lolen, &hilen);
- \end{verbatim}
-
- {\tt hiaddr} and {\tt loaddr} are the top and bottom 16-bits (respectively) of
- the 32-bit address of the buffer. {\tt hilen} and {\tt lolen} are the two
- halves of its length.
-
- Due to a preculiarity of the binding you have to reassemble these
- pieces before using them. (The actual value of \verb"WF_SCREEN" is 17; this
- does not appear in some versions of the {\tt GEMDEFS.H} file.)
-
- If you use this buffer, you MUST prevent menus from dropping down by
- using either the \verb"BEG_UPDATE" or \verb"BEG_MCTRL" \verb"wind_update" calls. Failure
- to do so will result in your data being destroyed. Remember to use
- the matching \verb"wind_update": \verb"END_UPDATE" or \verb"END_MCTRL",
- when you are done.
-
- The other useful call enables you to replace the system's desktop
- definition with a resource of your choosing. The call:
-
- \begin{verbatim}
- wind_set(0, WF_NEWDESK, tree, 0, 0);
- \end{verbatim}
-
- where {\tt tree}\/ is the 32-bit address of the object tree, will cause the
- AES to draw your definition instead of the usual gray or green
- background. Not only that, it will continue to redraw this tree with
- no intervention on your part.
-
- Obviously, the new definition must be carefully built to fit the
- desktop area exactly or garbage will be left around the edges. For
- the truly sophisticated, a user-defined object could be used in this
- tree, with the result that your application's code would be entered
- from the AES whenever the desktop was redrawn. This would allow you
- to put VDI pictures or complex images onto the desktop background.
-
- This concludes our tour of GEM's basic window management techniques.
- There have been some unavoidable glimpses of paths not yet taken
- (forward references), but we will discuss those soon.
-
- In the next chapter, we will take a look at techniques for handling
- simple dialog boxes, and start exploring the mysteries of resources
- and object trees.
-
-
- \newpage
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \section{The Dialog Handler}
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{A meaningful dialog}
-
- We will now start an exploration of ST GEM's dialog
- handler. I will discuss basic system calls for presenting the
- dialog, and then continue with techniques for initializing and
- reading on/off button and "radio" button objects. We will also take
- some short side-trips into the operation of the GEM Resource
- Construction Set to assist you in building these dialogs.
-
- There are a number of short C routines which accompany this chapter.
- These are found in {\tt GEMCL03.C}.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Defining terms}
-
- A dialog box is an "interactive form" in which the user may enter
- text and indicate selections by pointing with the mouse. Dialogs in
- GEM are "modal", that is, when a dialog is activated other screen
- functions such as menus and window controls are suspended until the
- dialog is completed.
-
- In most cases, the visual structure of a GEM dialog is specified
- within your application's resource file. The GEM Resource
- Construction Set (RCS) is used to build a picture of the dialog.
-
- When the RCS writes out a resource, it converts that picture into a
- tree of GEM drawing objects and stores this data structure within the
- resource. Before your application can display the dialog, it must
- load this resource file and find the address of the tree which
- defines the dialog.
-
- To load a resource, the AES checks its size and allocates memory for
- the load. It then reads in the resource, adjusting internal pointers
- to reflect the load address. Finally, the object sizes stored in the
- resource are converted from characters to pixels using the system
- font size.
-
- (A note for those with Macintosh experience: Although Mac and GEM
- resources share a name, there are fundamental differences which can
- be misleading. A Mac resource is a fork within a file; a GEM
- resource is a TOS file by itself. Mac resources may be paged in and
- out of memory; GEM resources are monolithic. GEM resources are
- internally tree structured; Mac resources are not. Finally, Mac
- resources include font information, while ST GEM does this with font
- loading at the VDI level.)
-
- The resource load is done with the GEM AES call:
-
- \begin{verbatim}
- ok = rsrc_load(ADDR("MYAPP.RSC"));
- \end{verbatim}
-
- {\tt MYAPP}\/ should be replaced with the name of your program. Resources
- conventionally have the same primary name as their application, with
- the RSC extent name instead of PRG. The {\tt ok}\/ flag returned by
- \verb"rsrc_load" will be FALSE is anything went wrong during the load.
-
- The most common causes of failure are the resource not being in the
- application's subdirectory, or lack of sufficient memory for GEM to
- allocate space for the resource. If this happens, you must terminate
- the program immediately.
-
- Once you have loaded the resource, you find the address of a dialog's
- object tree with:
-
- \begin{verbatim}
- rsrc_gaddr(R_TREE, MYDIALOG, &tree);
- \end{verbatim}
-
- {\tt tree} is a 32-bit variable which will receive the address of the root
- node of the tree.
-
- The mnemonic \verb"MYDIALOG" should be replaced with the name you gave your
- dialog when defining it in the RCS. At the same time that it writes
- the resource, RCS generates a corresponding {\tt .H} file containing tree
- and object names. In order to use these mnemonics within your
- program, you must include the name file in your compile:
-
- \begin{verbatim}
- #include "MYAPP.H"
- \end{verbatim}
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Bug Alert!}
-
- When using the DRI/Alcyon C compiler, {\tt .H} files must be in the
- compiler's home directory or they will not be found. This is
- especially annoying using a two floppy drive ST development system.
- The only way around this is to explicitly reference an alternate
- disk in the \verb"#include", for instance: \verb"B:MYAPP.H"
-
- Now that the address of the dialog tree has been found, you are ready
- to display it. The standard (and minimal) sequence for doing so is
- given in routine \verb"hndl_dial()" in {\tt GEMCL03.C}. We will now walk
- through each step in this procedure.
-
- The \verb"form_center" call establishes the location of the dialog on the
- screen. Dialog trees generated by the RCS have an undefined origin
- (upper-left corner).
-
- \verb"Form_center" computes the upper-left location necessary to center the
- dialog on the screen, and inserts it into the \verb"OB_X" and \verb"OB_Y" fields of
- the \verb"ROOT" object of the tree. It also computes the screen rectangle
- which the dialog will occupy on screen and writes its pixel
- coordinates into variables {\tt xdial}, {\tt ydial}, {\tt wdial}, and {\tt hdial}.
-
- There is one peculiarity of \verb"form_center" which occasionally causes
- trouble. Normally the rectangle returned in {\tt xdial}, etc., is exactly
- the same size as the basic dialog box.
-
- However, when the \verb"OUTLINED" enhancement has been specified for the
- box, \verb"form_center" adds a three pixel margin to the rectangle returned.
- This causes the screen area under the outline to be correctly redrawn
- later (see below). Note that \verb"OUTLINED" is part of the standard dialog
- box in the RCS. Other enhancements, such as \verb"SHADOWED" or "outside"
- borders are NOT handled in this fashion, and you must compensate for
- them in your code.
-
- The next part of the sequence is a \verb"form_dial" call with a zero
- parameter. This reserves the screen for the dialog action about to
- occur. Note that the C binding given for \verb"form_dial" in the DRI
- documents is in error: there are nine parameters, not five. The
- first set of xywh arguments is actually used with \verb"form_dial" calls 1
- and 2 only, but place holders must be supplied in all cases.
-
- The succeeding \verb"form_dial" call (parameter one) animates a "zoom box"
- on the screen which moves and grows from the first screen rectangle
- given to the second rectangle, where the dialog will be displayed.
-
- The use of this call is entirely optional. In choosing whether to
- use it or not, you should consider whether the origin of the "zoom"
- is relevant to the operation. For instance, a zoom from the menu
- bar is relatively meaningless, while a zoom from an object about to
- be edited in the dialog provides visual feedback to the user, showing
- whether the correct object was chosen.
-
- If the origin is not relevant, then the zoom is just a time-waster.
- If you decide to include these effects, consider a "preferences"
- option in your app which will allow the experienced and jaded user
- to turn them off in the interests of speed.
-
- The \verb"objc_draw" call actually displays the dialog on the screen. Note
- that the address of the tree, the beginning drawing object, and the
- drawing depth are passed as arguments, as well as the rectangle
- allotted for the dialog.
-
- In general, dialogs (and parts of dialogs) are ALWAYS drawn
- beginning at the ROOT (object zero). When you want to draw only a
- portion of the dialog, adjust the clipping rectangle, but not the
- object number. This ensures that the background of the dialog is
- always drawn correctly.
-
- The \verb"objc_xywh()" utility in the listing can be used to find the
- clipping rectangle for any object within a dialog, though you may
- have to allow an extra margin is you have used shadows, outlines, or
- outside borders with the object.
-
- Calling \verb"form_do" transfers control to the AES, which animates the
- dialog for user interaction. The address of the dialog tree is
- passed as a parameter. The second paramter is the number of the
- editable object at which the text cursor will first be positioned.
- If you have no text fields, pass a zero. Note that again the DRI
- documents are in error: passing a -1 default may crash the system.
- Also be careful that the default which you specify is actually a text
- field; no error checking is performed.
-
- The \verb"form_do" call returns the number of the object on which the user
- clicked to terminate the dialog. Usually this is a button type
- object with the \verb"EXIT" and \verb"SELECTABLE" attributes set. Setting the
- \verb"DEFAULT" attribute as well will cause an exit on that object is a
- carriage return is struck while in the dialog.
-
- If the top bit of the return is set, it indicates that the exit
- object had the \verb"TOUCHEXIT" attribute and was selected with a
- double-click. Since very few dialogs use this combination, the
- sample code simply masks off the top bit.
-
- The next \verb"form_dial" call reverses the "zoom box", moving it from the
- dialog's location back to the given x,y,w,h. The same cautions apply
- here as above.
-
- The final \verb"form_dial" call tells GEM that the dialog is complete, and
- that the screen area occupied by the dialog is now considered "dirty"
- and needs to be redrawn. Using the methods described in the previous
- chapter, GEM then sends redraws to all windows which were overlaid,
- and does any necessary redrawing of the menu or desktop itself.
-
- There is one notable "feature" of \verb"form_dial(3)": It always redraws
- an area which is two pixels wider and higher than your request! This
- was probably included to make sure that drop-shadows were cleaned up,
- and is usually innocuous.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{A handy trick}
-
- Use of the \verb"form_dial(3)" call is not limited to dialogs. You can
- use it to force the system to redraw any part of the screen. The
- advantage of this method is that the redraw area need not lie
- entirely within a window, as was necessary with the {\tt send\_redraw}\/
- method detailed in the last chapter. A disadvantage is that this
- method is somewhat slower, since the AES has to decide who gets the
- redraws.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Clean up}
-
- As a last step, you need to clear the \verb"SELECTED" flag in the object
- which was clicked. If you do not do this, the object will be drawn
- inverted the next time you call the dialog. You could clear the flag
- with the GEM \verb"objc_change" call, but it is inefficient since you do
- not need to redraw the object.
-
- Instead, use the \verb"desel_obj()" code in the listings, which modifies
- the object's \verb"OB_STATE" field directly. Assuming that {\tt ret\_obj}
- contains the exit object returned by \verb"hndl_dial", the call:
-
- \begin{verbatim}
- desel_obj(tree, ret_obj);
- \end{verbatim}
-
- will do the trick.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Recap}
-
- The basic dialog handling method I have described contains three
- steps: initialization ({\tt rsrc\_gaddr}\/), dialog presentation ({\tt hndl\_dial}\/),
- and cleanup ({\tt desel\_obj}\/).
-
- As we build more advanced dialogs, these same basic steps will be
- performed, but they will grow more complex. The initialization will
- include setting up proper object text and states, and the cleanup
- phase will also interrogate the final states of objects to find out
- what the user did.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Button, button}
-
- The simple dialogs described above contain only exit buttons as
- active objects. As such, they are little more than glorified alert
- boxes.
-
- We will now increase the complexity a little by considering non-exit
- buttons. These are constructed by setting the \verb"SELECTABLE" attribute
- on a button object. At run-time, such an object will toggle its
- state between selected (highlighted) and non-selected whenever the
- user clicks on it. (You can set the \verb"SELECTABLE" attribute of other
- types of objects and use them instead of actual buttons, but be sure
- that the user will be able to figure out what you intend!)
-
- Having non-exit buttons forces us to consider the problem of
- initializing them before the dialog, and interrogating and resetting
- them afterward.
-
- Since a button is a toggle, it is usually associated with a flag
- variable in the program. As part of the initialization, you should
- test the flag variable, and if true call:
-
- \begin{verbatim}
- sel_obj(tree, BTNOBJ);
- \end{verbatim}
-
- which will cause the button to appear highlighted when the dialog is
- first drawn. \verb"Sel_obj()" is in the listing. \verb"BTNOBJ" is replaced with
- the name you gave your button when you defined it in the RCS. Since
- the button starts out deselected, you don't have to do anything if
- your flag variable is false.
-
- After the dialog has completed, you need to check the object's state.
- The \verb"selectp()" utility does so by masking the \verb"OB_STATE" field. You can
- simply assign the result of this test to your flag variable, but be
- sure that the dialog was exited with an OK button, not with a CANCEL!
- Again, remember to clean up the button with \verb"desel_obj()". (It's often
- easiest to deselect all buttons just before you leave the dialog
- routine, regardless of the final dialog state.)
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Who's got the button?}
-
- Another common use of buttons in a dialog is to select one of a set
- of possible options. In GEM, such objects are called radio buttons.
- This term recalls automobile radio tuners where pushing in one button
- pops out any others. In like fashion, selecting any one of a set of
- radio buttons automatically deselects all of the others.
-
- To use the radio button feature, you must do some careful work with
- the Resource Construction Set.
-
- First, each member of a set of radio buttons must be children of the
- same parent object within the object tree. To create this structure,
- put a hollow box type object in the dialog, make it big enough to
- hold all of the buttons, and then put the buttons into the box one at
- a time.
-
- By nesting the buttons within the box object, you force them to be
- its children. Each of the buttons must have both the \verb"SELECTABLE" and
- \verb"RADIO BUTTON" attributes set. When you are done, you may make the
- containing box invisible by setting its border to zero, {\em but do not
- FLATTEN it!}
-
- Since each radio button represents a different option, you must
- usually assign a name to each object. When initializing the dialog,
- you must check which option is currently set, and turn on the
- corresponding button only. A chain of if-then-else structures
- assures that only one button will be selected.
-
- At the conclusion of the dialog, you must check each button with
- \verb"selectp()" and make the appropriate adjustments to internal variables.
- Again, an if-then-else chain is appropriate since only one button may
- be selected. Either deselect the chosen button within this chain or
- do them all at the end.
-
- There is one common use of radio buttons in which you may short-cut
- this procedure. If the buttons each represent one possible value of
- a numeric variable, for instance, a set of selector buttons
- representing colors from zero to seven, then you can compute the
- initial object directly.
-
- In order for this technique to work, you must use a special
- capability of the RCS. Insert the object corresponding to a zero
- value at the top (or left) of your array of buttons, then put the
- "one" button below (or right) of it, and so on.
-
- When the buttons are complete, the SORT operation is used to
- guarantee that the top/left object is in fact the first child of the
- parent box with the others following in order. Due to the details of
- object tree structure (to be discussed in the next column), this will
- guarantee that these objects are contiguous in the resource.
-
- If you assign a name (say BUTTON1) to the first button, then you can
- initialize the correct button with the call:
-
- \begin{verbatim}
- sel_obj(tree, BUTTON1 + field);
- \end{verbatim}
-
- where field is the variable of interest.
-
- When the dialog is complete, you can scan the radio buttons to
- compute the new value for the underlying variable. The \verb"encode()"
- procedure in the listing will do this. As always, remember to
- deselect the buttons at the end.
-
- You can use offsets or multipliers if your variable's values don't
- start with zero or increment by one. If the values are irregular you
- may be able to use a lookup table, at the cost of additional code.
-
- Next, I will discuss the internal structure of object
- trees. Then we'll use that knowledge to build a piece of code which
- will "walk" an entire tree and apply a function to each object.
- We'll apply this code to do all of the button deselects with a
- single call! I'll also look at handling editable text fields and
- discuss some ways to alter a dialog's appearance at run-time.
-
- \newpage
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \section{Object Trees}
-
- We are about to delve into the mysteries of GEM resource structure,
- and then use this knowledge to create some useful utilities for
- handling dialogs. As before, there is once again a demonstration file.
- You will find it under the name {\tt GEMCL04.C}.
-
- The first and largest part of the listing contains a C image of a
- sample resource file. To create this listing, I used the GEM
- Resource Construction Set to create a dummy resource with three
- dialogs including examples of all object types, then enabled the C
- output option and saved the resource. If you have access to a copy
- of RCS, I suggest that you create your own listing in order to get a
- feel for the results. Then, using either listing as a roadmap to
- the resource, you can follow along as we enter...
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{A maze of twisty little passages}
-
- While a GEM resource is loaded as a block of binary information, it
- is actually composed of a number of different data structures. These
- structures are linked together in a rather tangled hierarchy. Our
- first job is to map this linkage system.
-
- The topmost structure in a resource file is the resource header.
- This is an array of words containing the size and offset within the
- resource of the other structures which follow. This information is
- used by GEM during the resource load process, and you should never
- need to access it. (The resource header does not appear in the C
- output file; it is generated by the RSCREATE utility if the C file is
- used to recreate the resource.)
-
- The next structure of interest is the tree index. This is an
- array of long pointers, each of which addresses the beginning of an
- object tree. Again, you wouldn't normally access this structure
- directly. The GEM \verb"rsrc_gaddr" call uses it when finding trees'
- addresses. This structure is called "rs\_trindex" in the C output.
-
- If you look at the contents of {\tt rs\_trindex}\/you will notice that the
- values are integers, instead of the pointers I described. What has
- happened is that RCS has converted the pointers to indices into the
- object array. (If you actually used the C file to recreate the
- resource file, then the pointers would be regenerated by RSCREATE.)
-
- Now you can follow the link from {\tt rs\_trindex}\/ to the objects stored
- in {\tt rs\_object}. Take (for instance) the second entry in {\tt rs\_trindex}\/ and
- count down that many lines in {\tt rs\_object}. The following line (object)
- should start with a -1. This indicates that it is the root object of
- a tree. The following objects down to the next root belong to that
- tree. We'll pass over the details of inter-object linkage for now,
- leaving it for later.
-
- There are a number of different fields in an object, but right now
- we'll concentrate on two of them: \verb"OB_TYPE" and \verb"OB_SPEC". The \verb"OB_TYPE"
- is the field which contains mnemonics like \verb"G_STRING" and \verb"G_BOX"
- indicating the type of the object. The \verb"OB_SPEC" is the only field in
- each object which is a LONG - you can tell it by the L after the
- number.
-
- What's in \verb"OB_SPEC" depends on the object type, so we need to talk
- about what kinds of objects are available, what you might use them
- for, and finally how they use the \verb"OB_SPEC" field.
-
- The box type objects are \verb"G_BOX", \verb"G_IBOX", and \verb"G_BOXCHAR". A \verb"G_BOX" is
- an opaque rectangle, with an optional border. It's used to create a
- solid patch of color or pattern on which to place other objects. For
- instance, the background of a dialog is a \verb"G_BOX".
-
- A \verb"G_IBOX" is a hollow box which has only a border. (If the border
- has no thickness, then the box is "invisible", hence the name.) The
- favorite use for IBOXes is to hold radio buttons. There is also one
- neat trick you can play with an IBOX. If you have more than one
- object (say an image and a string) which you would like to have
- selected all at once, you can insert them in a dialog, then cover
- them with an IBOX. Since the box is transparent, they will show
- through. If you now make the box selectable, clicking on it will
- highlight the whole area at once!
-
- The \verb"G_BOXCHAR" is just like a \verb"G_BOX", except that a single character
- is drawn in its center. They are mostly used as "control points":
- the \verb"FULLER", \verb"CLOSER", \verb"SIZER", and arrows in GEM windows are BOXCHARs, as
- are the components of the color selection gadgets in the RCS.
-
- The \verb"OB_SPEC" for box type objects is a packed bit array. Its
- various fields contain the background color and pattern, the border
- thickness and color, and the optional character and its color.
-
- The string type objects are \verb"G_STRING", \verb"G_BUTTON", and \verb"G_TITLE".
- \verb"G_STRINGs" (in addition to being a bad pun) are for setting up static
- explanatory text within dialogs. The characters are always written
- in the "system font": full size, black, with no special effects.
-
- We have already discussed many of the uses of \verb"G_BUTTONs". They add
- a border around the text. The thickness of a \verb"G_BUTTON"'s border is
- determined by what flags are set for the object. All buttons start
- out with a border thickness of one pixel. One pixel is added if the
- \verb"EXIT" attribute is set, and one more is added if the \verb"DEFAULT" attribute
- is set.
-
- The \verb"G_TITLE" type is a specially formatted text string used only in
- the title bar of menus. This type is needed to make sure that the
- menus redraw correctly. The Resource Construction Set automatically
- handles inserting \verb"G_TITLEs", so you will seldom use them directly.
-
- In a resource, the \verb"OB_SPEC" for all string objects is a long
- pointer to a null terminated ASCII string. The string data in the C
- file is shown in the BYTE array {\tt rs\_strings}. Again you will notice
- that the \verb"OB_SPECs" in the C file have been converted to indices into
- {\tt rs\_string}. To find the string which matches the object, take the
- value of \verb"OB_SPEC" and count down that many lines in {\tt rs\_strings}.
- The next line is the correct string.
-
- The formatted text object types are \verb"G_TEXT", \verb"G_BOXTEXT", \verb"G_FTEXT",
- and \verb"G_FBOXTEXT". \verb"G_TEXTs" are a lot like strings, except that you can
- specify a color, different sizes, and a positioning rule for the
- text. Since they require more memory than \verb"G_STRINGs", \verb"G_TEXTs" should
- be used sparingly to draw attention to important information within
- a dialog. \verb"G_TEXTs" are also useful for automatic centering of dialog
- text which is changed at run-time. I will describe this technique
- in detail later on.
-
- The \verb"G_BOXTEXT" type adds a solid background and border to the
- \verb"G_TEXT" type. These objects are occasionally used in place of
- \verb"G_BUTTONs" when their color will draw attention to an important
- object.
-
- The \verb"G_FTEXT" object is an editable text field. You are able to
- specify a constant "template" of characters, a validation field for
- those characters which are to be typed in, and an initial value for
- the input characters. You may also select color, size, and
- positioning rule for \verb"G_FTEXTs". We'll discuss text editing at length
- below.
-
- The \verb"G_FBOXTEXT" object, as you might suspect, is the same as
- \verb"G_FTEXT" with the addition of background and border. This type is
- seldom used: the extra appearance details distract attention from the
- text being edited.
-
- The \verb"OB_SPEC" for a formatted text object is a pointer to yet
- another type of structure: a \verb"TEDINFO". In the C file, you will find
- these in {\tt rs\_tedinfo}. Take the \verb"OB_SPEC" value from each text type
- object and count down that many entries in {\tt rs\_tedinfo}, finding the
- matching \verb"TEDINFO" on the next line. Each contains pointers to ASCII
- strings for the template, validation, and initialization. You can
- find these strings in {\tt rs\_strings}, just as above.
-
- There are also fields for the optional background and border
- details, and for the length of the template and text. As we will see
- when discussing editing, the most important \verb"TEDINFO" fields are the
- \verb"TE_PTEXT" pointer to initialized text and the \verb"TE_TXTLEN" field which
- gives its length.
-
- The \verb"G_IMAGE" object type is the only one of its kind. A \verb"G_IMAGE" is
- a monochrome bit image. For examples, see the images within the
- various GEM alert boxes. Note that monochrome does not necessarily
- mean black. The image may be any color, but all parts of it are the
- SAME color. \verb"G_IMAGEs" are used as visual cues in dialogs. They are
- seldom used as selectable items because their entire rectangle is
- inverted when they are clicked. This effect is seldom visually
- pleasing, particularly if the image is colored.
-
- \verb"G_IMAGE" objects have an \verb"OB_SPEC" which is a pointer to a further
- structure type: the \verb"BITBLK". By now, you should guess that you will
- find it in the C file in the array {\tt rs\_bitblk}. The \verb"BITBLK" contains
- fields describing the height and width of the image in pixels, its
- color, and it also contains a long pointer to the actual bits which
- make up the image. In the C file, the images are encoded as
- hexadecimal words and stored in arrays named IMAG0, IMAG1, and so on.
-
- The last type of object is the \verb"G_ICON". Like the \verb"G_IMAGE", the
- \verb"G_ICON" is a bit image, but it adds a mask array which selects what
- portions of the image will be drawn, as well as an explanatory text
- field. A \verb"G_ICON" may also specify different colors for its
- "foreground" pixels (the ones that are normally black), and its
- "background" pixels (which are normally white).
-
- The pictures which you see in Desktop windows are \verb"G_ICONs", and so
- are the disks and trashcan on the desktop surface. With the latter
- you will notice the effects of the mask. The desktop shows through
- right up to the edge of the \verb"G_ICON", and only the icon itself (not a
- rectangle) is inverted when a disk is selected.
-
- The \verb"OB_SPEC" of an icon points to another structure called an
- \verb"ICONBLK". It is shown in the C file as {\tt rs\_iconblk}. The \verb"ICONBLK"\/
- contains long pointers to its foreground bit array, to the mask bit
- array, and to the ASCII string of explanatory text. It also has the
- foreground and background colors as well as the location of the text
- area from the upper left of the icon. The most common use of
- \verb"G_ICONs" and \verb"ICONBLKs" is not in dialogs, instead they are used
- frequently in trees which are build at run-time, such as Desktop
- windows. In an upcoming chapter, we will return to a discussion of
- building such "on-the-fly" trees with \verb"G_ICONs".
-
- Now, let's recap the hierarchy of resource structures: The
- highest level structures are the resource header, and then the tree
- index. The tree index points to the beginning of each object tree.
- The objects making up the tree are of several types, and depending on
- that type, they may contain pointers to ASCII strings, or to \verb"TEDINFO",
- \verb"ICONBLK", or \verb"BITBLK" structures. \verb"TEDINFOs" contain further pointers to
- strings; \verb"BITBLKs" have pointers to bit images; and \verb"ICONBLKs" have both.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Putting it to work}
-
- The most common situations requiring you to
- understand resource structures involve the use of text and editable
- text objects in dialogs. We'll look at two such techniques.
-
- Often an application requires two or more dialogs which are very
- similar except for one or two title lines. In this circumstance, you
- can save a good deal of resource space by building only one dialog,
- and changing the title at run time.
-
- It is easy to go wrong with this practice, however, because the
- obvious tactic of using a \verb"G_STRING" and writing over its text at run
- time can go wrong. The first problem is that you must know in
- advance the longest title to be used, and put a string that long into
- the resource. If you don't you will damage other objects in the
- resource as you copy in characters. The other problem is that a
- \verb"G_STRING" is always drawn at the same place in a dialog. If the
- length of the title changes from time to time, the dialog will have
- an unbalanced and sloppy appearance.
-
- A better way to do this is to exploit the \verb"G_TEXT" object type, and
- the \verb"TEDINFO" structure. The \verb"set_text()" routine in {\tt GEMCL04.C}
- shows
- how. The parameters provided are the tree address, the object
- number, and the 32-bit address of the string to be substituted. For
- this to work, the object referenced should be defined as a \verb"G_TEXT"
- type object. Additionally, the Centered text type should be chosen,
- and the object should have been "stretched" so that it fills the
- dialog box from side to side.
-
- In the code, the first action is to get the \verb"OB_SPEC" from the
- object which was referenced. Since we know that the object is a
- \verb"G_TEXT", the \verb"OB_SPEC" must point to a \verb"TEDINFO". We need to change two
- fields in the \verb"TEDINFO". The \verb"TE_PTEXT" field is the pointer to the
- actual string to be displayed; we replace it with the address of our
- new string. The \verb"TE_TXTLEN" field is loaded with the new string's
- length. Since the Centered attribute was specified for the object,
- changing the \verb"TE_TXTLEN" will cause the string to be correctly
- positioned in the middle of the dialog!
-
- Editing text also requires working with the \verb"TEDINFO" structure. One
- way of doing this is shown in the example. The object to be used
- (EDITOBJ) is assumed to be a \verb"G_FTEXT" or \verb"G_FBOXTEXT". Since we will
- replace the initialized text at run time, that field may be left
- empty when building the object in the RCS.
-
- The basic trick of this code is to point the \verb"TEDINFO"'s \verb"TE_PTEXT" at
- a string which is defined in your code's local stack. The advantages
- of this technique are that you save resource space, save static data
- by putting the string in reusable stack memory, and automatically
- create a scratch string which may be discarded if the dialog is
- cancelled.
-
- The text string shown is arbitrarily 41 characters long. You
- should give yours a length equal to the number of blanks in the
- object's template field plus one. Note that the code is shown as a
- segment, rather than a subroutine. This is required because the
- text string must be allocated within the context of dialog handling
- routine itself, rather than a routine which it calls!
-
- After the tree address is found, the code proceeds to find the
- \verb"TEDINFO" and modify its \verb"TE_PTEXT" as described above. However, the
- length which is inserted into \verb"TE_TXTLEN" must be the maximum string
- length, including the null!
-
- The final line of code inserts a null into the first character of
- the uninitialized string. This will produce an empty editing field
- when the dialog is displayed. If there is an existing value for the
- object, you should instead use \verb"strcpy()" to move it into \verb"text[]". Once
- the dialog is complete, you should check its final status as
- described in the last section. If an "OK" button was clicked, you
- will then use \verb"strcpy()" to move the value in \verb"text[]" back to its static
- location.
-
- Although I prefer this method of handling editable text, another
- method deserves mention also. This procedure allocates a full length
- text string of blanks when creating the editable object in the RCS.
- At run-time, the \verb"TE_PTEXT" link is followed to find this string's
- location in the resource, and any pre-existing value is copied in.
- After the dialog is run, the resulting value is copied back out if
- the dialog completed successfully.
-
- Note that in both editing techniques a copy of the current string
- value is kept within the application's data area. Threading the
- resource whenever you need to check a string's value is extremely
- wasteful.
-
- The validation string normally contains the same character repeated
- a great many times. Fortunately that is not necessary. AES duplicates
- the last character of the string as often as it is needed.
-
- All masks except 'X', 'a' and 'n' convert valid characters to
- uppercase. Unfortunately foreign characters are not converted when using
- the 'f' or 'F' masks.
-
- \begin{description}
- \item[X] all characters
- \item[x] all characters are converted to uppercase
- \item[9] 0..9
- \item[A] A..Z, foreign characters
- \item[a] a..z A..Z foreign characters
- \item[N] A..Z 0..9
- \item[n] a..z 0..9 A..z foreign characters
- \item[P] 0..9 A..Z foreign characters \verb"\ ? * : . _"
- \item[p] 0..9 A..Z foreign characters \verb"\ : . _"
- \item[F] a..z 0..9 A..Z foreign characters \verb": ? * _" (no '.' but ':')
- \item[f] a..z 0..9 A..Z foreign characters \verb"_" (no '.')
- \end{description}
-
- One final note on editable text objects: GEM's editor uses the
- commercial at sign '\verb"@"' as a "meta-character". If it is the first
- byte of the initialized text, then the field is displayed blank no
- matter what follows. This can be useful, but is sometimes confusing
- when a user in all innocence enters an \verb"@" and has his text disappear
- the next time the dialog is drawn!
-
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Back to an omission}
-
- So, we established that resources trees are
- pointed to by the tree index, and that they are composed of objects
- which contain pointers onward to other structures. However, we
- passed over the issue of linkage among the objects within a tree. It
- is now time to go back and cure this omission.
-
- The technical term for the linkage scheme of an object tree is a
- "right-threaded binary tree". If you already know what this is, you
- can skim over the next few paragraphs. If you happen to have access
- to a copy of the book "FUNDAMENTAL ALGORITHMS", which is part of the
- series THE ART OF COMPUTER PROGRAMMING by Donald E. Knuth, you might
- want to read his excellent discussion of binary trees beginning on
- page 332.
-
- For the following discussion, you should have a listing of the C
- image of a resource tree in front of you. Before we begin, I should warn you of one
- peculiarity of "computer trees": They grow upside-down! That is,
- when they are diagrammed or described, their root is at the top, and
- the "leaves" grow downward. You will see this both in the listing,
- and in the way the following discussion talks about moving through
- trees.
-
- Each GEM object tree begins at its ROOT object, numbered zero,
- which is the object pointed at by the tree index. There are three
- link fields at the beginning of each object. They are called
- \verb"OB_NEXT", \verb"OB_HEAD", and \verb"OB_TAIL", which is the order in which they
- appear.
-
- Each of the links is shown as an index relative to the root of
- the current tree. This means that the link '0' would refer to the
- root of the tree, while '2' would indicate the object two lines below
- it. The special link -1 is called NIL, and means that there is no
- link in the given direction.
-
- Each object, or node, in a tree may have "offspring" or nodes
- which are nested below it. If it does, then its \verb"OB_HEAD" will point
- to its first (or "leftmost") "child", while the \verb"OB_TAIL" will point to
- the last ("rightmost") of its offspring. The \verb"OB_NEXT" pointer links
- the children together, with the \verb"OB_NEXT" of the first pointing to the
- second, and so on, until the \verb"OB_NEXT" of the last finally points back
- to its parent, the object at which we started.
-
- Remember that each of these children may in turn have offspring
- of their own, so that the original "parent" may have a large and
- complex collection of "descendents".
-
- Let's look at the first tree in the listing to see an example
- of this structure. The very first object is the ROOT. Note that its
- \verb"OB_NEXT" is NIL, meaning that there are no more objects in the tree:
- the ROOT is both the beginning and the end of the tree. In this
- case, the \verb"OB_HEAD" is 1 and the \verb"OB_TAIL" is 3, showing that there are
- at least two different children.
-
- Following \verb"OB_HEAD" down to the next line, we can trace through
- the \verb"OB_NEXT" links (2, 3, 0) as they lead through a total of three
- children and back to the ROOT. You will notice that the first two
- children have NIL for the \verb"OB_HEAD" and \verb"OB_TAILs", indicating that they
- have no further offspring.
-
- However, node three, the last child of the ROOT, does have the
- value 4 for both its \verb"OB_HEAD" and \verb"OB_TAIL". By this we can tell that
- it has one, and only one, offspring. Sure enough, when we look at
- node four, we see that its \verb"OB_NEXT" leads immediately back to node
- three. Additionally, it has no further offspring because its \verb"OB_HEAD"
- and \verb"OB_TAIL" are NIL.
-
- You will find that object trees are always written out by the
- Resource Construction Set in "pre-order". (Again, see Knuth if you
- have a copy.) This means that the ROOT is always written first, then
- its offspring left to right. This rule is applied recursively, that
- is, we go down to the next level and write out each of these nodes,
- then THEIR children left to right, and so on.
-
- For a further example, look at the next tree in \verb"rs_object" in the
- listing. You will see that the ROOT has an \verb"OB_HEAD" of 1 and an
- \verb"OB_TAIL" of 6, but that it actually has only three offspring (nodes 1,
- 2 and 6). We see that node 2 itself had children, and applying the
- rule given above, they were written out before continuing with the
- next child of the ROOT.
-
- Why was this seemingly complex structure chosen for GEM? The
- reason has do with the tasks of drawing objects in their proper
- locations on the screen, and determining which object was "hit" when
- a mouse click is detected.
-
- To find out how this works, we must look at four more fields
- found in each object: \verb"OB_X", \verb"OB_Y", \verb"OB_WIDTH", and \verb"OB_HEIGHT". These
- fields are the last four on each line in the sample trees.
-
- Each object in a tree "owns" a rectangle on the screen. These
- fields define that rectangle. When a resource is stored "outside"
- the program the fields are in character units, so that an object
- with \verb"OB_WIDTH" of 10 and \verb"OB_HEIGHT" of 2 (for instance) would define a
- screen area 10 characters wide and 2 high.
-
- When the resource is read into memory with an \verb"rsrc_load" call,
- GEM multiplies the appropriate character dimension in pixels into
- each of these fields. In this way portability is achieved: the same
- resource file works for any of the ST's three resolutions. Knowing
- how \verb"rsrc_load" works, your code should treat these fields as pixel
- coordinates.
-
- (I have committed one oversimplification above. If an object is
- not created on a character boundary in the RCS, then the external
- storage method described will not work. In this case, the lower byte
- of each rectangle field is used to store the nearest character
- position, while the upper byte stores the pixel remainder to be added
- after the character size is multiplied in.
-
- Non-character-boundary objects may only be created in the "FREE"
- tree mode of the Resource Construction Set (also called "PANEL" in
- RCS 2.0). You should use them only in programs which will run in a
- single ST screen mode, because pixel coordinates are not portable
- between resolutions.
-
- There is a bug in AES that may cause some problems: when \verb"rsrc_load"
- or \verb"rsrc_obfix" convert from pixel to character coordinates, they
- check for an \verb"OB_WIDTH" value of 80 characters and set the pixelwidth
- to the screenwidth. On a monitor that can display more than 80 characters
- in a row, an object of 80 characters can actually be larger than an object
- of 90 characters! That's because if no value of 80 is found, AES multiplies
- the value with the character size. Then the pixel offset from the high byte
- is added.)
-
- The first real secret of object rectangles is that each \verb"OB_X" and
- \verb"OB_Y" is specified RELATIVE to the X and Y coordinate of its parent
- object within the tree. This is the first property we have seen
- that is actually "inherited" from level to level within the tree.
-
- The second secret is more subtle: Every object's rectangle must
- be entirely contained within the rectangle of its parent. This
- principle goes by the names "bounding rectangles" or "visual
- hierarchy". We'll see in a moment how useful it is when detecting
- mouse/object collisions.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{How GEM does it}
-
- Knowing these secrets, and the linkage structure of object
- trees, we can deduce how a number of the GEM operations must work.
- For instance, consider \verb"objc_offset", which returns the actual screen
- X and Y of an object. We can see now that simply loading the \verb"OB_X"
- and \verb"OB_Y" fields of the object does not suffice: they only give the
- offset relative to the parent object. So, \verb"objc_offset" must BEGIN
- with these values, and then work its way back up to the ROOT of the
- tree, adding in the offsets found at each level.
-
- This can be done by following the \verb"OB_NEXT" links from the chosen
- object. Whenever \verb"OB_NEXT" points to an object whose \verb"OB_TAIL" points
- right back to the same location, then the new node is another level,
- or "parent" in the tree, and \verb"objc_offset" adds its \verb"OB_X" and \verb"OB_Y" into
- the running totals. When \verb"OB_NEXT" becomes NIL, then the ROOT has been
- reached and the totals are the values to return. (By the way,
- remember that the \verb"OB_X" and \verb"OB_Y" of the ROOT are undefined until
- \verb"form_center" has been called for the tree. They are shown as zeroes
- in the sample trees.)
-
- We can also figure out \verb"objc_draw". It works its way DOWN the
- tree, drawing each object as it comes to it. It, too, must keep a
- running X and Y variable, adding in object offsets as it descends
- tree levels (using \verb"OB_HEAD"), and subtracting them again as it returns
- from each level. Since the larger objects are nearer the ROOT, we
- can now see why they are drawn first, with smaller objects drawn
- later or "on top of" them.
-
- (If you write an application which needs to move portions of a
- dialog or screen with respect to each other, you can take advantage
- of inheritance of screen position in \verb"objc_draw". Simply by changing
- the \verb"OB_X" and/or \verb"OB_Y" of an object, you can move it and its entire
- sub-tree to a new location in the dialog. For instance, changing the
- coordinates of the parent box of a set of radio buttons will cause
- all of the buttons to move along with it.)
-
- \verb"Objc_draw" also gives us an example of the uses of visual
- hierarchy. Recall that a clipping rectangle is specified when calling
- \verb"objc_draw". At each level of the tree we know that all objects below
- are contained in the screen rectangle of the current object. If the
- current rectangle falls completely outside the specified clipping
- rectangle, we know immediately that we need not draw the object, or
- any of its descendents! This ability to ignore an entire subtree is
- called "trivial rejection".
-
- Now it's rather easy to figure out \verb"objc_find". It starts out by
- setting its "object found" variable to NIL. It begins a "walk"
- through the entire object tree, following \verb"OB_HEAD" and \verb"OB_NEXT" links,
- and keeping a current X and Y, just like \verb"objc_draw".
-
- At each node visited, it simply checks to see if the "mouse" X,Y
- specified in the call are inside the current object's rectangle. If
- they are, that object becomes the found object, and the tree walk
- continues with the object's offspring, and then siblings. Notice how
- this checking of offspring makes sure that a smaller object nested
- within, i.e., below, a larger object is found correctly.
-
- If the mouse X,Y position is not within the object being
- checked, then by visual hierarchy it cannot be within any of its
- offspring, either. Trivial rejection wins again, and the entire
- sub-tree is skipped! \verb"Objc_find" moves on to the \verb"OB_NEXT" of the
- rejected object.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Thought Experiments}
-
- Thinking about the \verb"objc_find" algorithm reveals some information
- about its performance, and a few tricks we may use in improving the
- appearance of dialogs and other object trees.
-
- First consider the problem of a dialog which contains many
- objects. If we lay them all out "side-by-side", then they will all be
- immediate offspring of the ROOT object. In this situation, the
- trivial rejection method will gain nothing. The time \verb"objc_find" takes
- to complete will vary linearly with the total number of objects.
- This is called an "Order N" process.
-
- Suppose that instead we broke up the dialog into two areas with
- invisible boxes, then broke up each of these areas in a like fashion,
- and so on until we got down to the size of the individual selectable
- objects. The number of bottom level objects in this scheme is a
- power of two equal to the depth of the tree. Trivial rejection is
- used to its fullest in this case. It is called an "Order Log N"
- process, and is much more efficient for large numbers of objects.
-
- In practice, the speed of the ST will allow you to ignore this
- distinction for most dialogs and other trees. But if you get into a
- situation when speed is critical in searching a large tree, remember
- that nesting objects can improve performance dramatically.
-
- If you have been following closely, you may have also noticed a
- hole in the visual hierarchy rule. It says that all of a node's
- children must lie within its rectangle, but it does NOT guarantee
- that the children's rectangles will be disjoint, that is, not
- overlap one another. This peculiarity is the basis of several useful
- tricks.
-
- First, remember that \verb"objc_find" always tries to scan the entire
- tree. That is, it doesn't quit when it finds the first object on the
- given coordinates. As mentioned above, this normally guarantees that
- nested objects will be found. Consider, however, what happens when
- the mouse coordinates are on a point where two or more objects {\em at
- the same level }\/ overlap: they will replace one another as the "found
- object" until \verb"objc_find" returns with the one which is "last", that
- is, rightmost in the tree.
-
- This quirk can be used to advantage in a number of cases.
- Suppose that you have in a dialog an image and a string which you
- would like to be selected together when either is clicked. Nesting
- within a common parent achieves nothing in this case. Instead,
- knowing that \verb"form_do" must use \verb"objc_find", you could use our trick.
-
- You have to know that the Resource Construction Set normally
- adds objects in a tree left to right, in the order in which you
- inserted them. You proceed to build the dialog in the following
- order: insert the image first, the string next, then carefully add
- an invisible box which is not nested within either, and size it to
- cover them both. Set the \verb"SELECTABLE" attribute for the box, and the
- dialog manager will find it, and invert the whole area, when either
- the image or string is clicked.
-
- By the way, remember that the SORT option in the RCS will change
- the order of an object's offspring. If you are going to try this
- trick, don't use SORT! It will undo all of your careful work.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{A treewalker of our own}
-
- Since the GEM system gets so much mileage out of walking object
- trees, it seems reasonable that the same method should be useful in
- application programs. In the {\tt GEMCL05.C} you will find \verb"map_tree()". As
- many LISP veterans might guess from the name, this code will traverse
- all or part of an object tree, applying a function to each node. It
- also allows the function to return a true/false value specifying
- whether the sub-tree below a particular node should be ignored.
- Let's examine \verb"map_tree()" in more detail as a final review of object
- tree structure.
-
- First, look at the parameters. "tree" is the long address of
- the object tree of interest, as retrieved by \verb"rsrc_gaddr". "this" is
- the node at which to begin the traverse, and "last" is the node at
- which to terminate.
-
- In most cases, the beginning node will be ROOT, and the final
- value will be NIL. This will result in the entire tree being
- traversed. You may use other values, but be sure that you CAN get to
- "last" from "this" by following tree links! Although \verb"map_tree()"
- includes a safety check to prevent "running off" the tree, you could
- get some very strange results from incorrect parameters.
-
- The declaration for the final parameter, "routine", makes use of
- C construct which may be new to some. It is a pointer to a
- subroutine which returns a WORD as a result.
-
- \verb"Map_tree()" begins by initializing a temporary variable, {\tt tmp1},
- which is used to store the number of the last node visited. Since no
- node will follow itself, setting {\tt tmp1}\/ to the starting node is safe.
-
- The main loop of the routine simply repeats visiting a new node
- until the last value is reached, or the safety check for end of tree
- is satisfied.
-
- At any node visited, we can be in one of two conditions. Either
- we are at a node which is "new", that is, not previously visited, or
- else we are returning to a parent node which has already been
- processed. We can detect the latter condition by comparing the last
- node visited (tmp1) with the \verb"OB_TAIL" pointer of the current node. If
- the node is "old", it is not processed a second time, we simply
- update tmp1 and continue.
-
- If the node is new, we call "routine" to process it, sending the
- tree address and object number as parameters. If a FALSE is
- returned, we will ignore any subtree below this node. On a TRUE
- return, we load up the \verb"OB_HEAD" pointer and follow it if a subtree
- exists. (If you don't care about rejecting subtrees, simply remove
- the if condition.) Finally, if the new node had no subtree, or was
- rejected by "routine", we follow along its \verb"OB_NEXT" link to the next
- node.
-
- A simple application of our new tool shows its power.
- You may recall the tedium of deselecting every button
- inside a dialog after it was completed. Using \verb"map_tree()", you can
- deselect EVERY OBJECT in the tree by using:
-
- \verb"map_tree(tree, ROOT, NIL, desel_obj);"
-
- You must use a slightly modified version of \verb"desel_obj()"
- which always returns TRUE. Be sure to
- define or declare \verb"desel_obj()" in your code BEFORE making the \verb"map_tree"
- call!
-
- In the next chapters, I will return to \verb"map_tree()" and show how it
- can be used for advanced techniques such as animated dialogs.
-
- \newpage
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \section{Raster operations}
-
- This chapter of ST Professional GEM is devoted to
- explaining the raster, or "bit-blit" portion of the Atari ST's VDI
- functions.
-
- Please note that this is NOT an attempt to show how to write
- directly to the video memory, although you will be able to deduce a
- great deal from the discussion. As usual, there is a listing to demonstrate
- the topics discussed under the name of {\tt GEMCL06.C}.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Defining terms}
-
- To understand VDI raster operations, you need to understand the
- jargon used to describe them. (Many programmers will be tempted to
- skip this section and go directly to the code. Please don't do it
- this time: Learning the jargon is the larger half of understanding
- the raster operations!)
-
- In VDI terms a {\tt raster area}\/ is simply a chunk of contiguous words
- of memory, defining a bit image. This chunk is called a "form". A
- form may reside in the ST's video map area or it may be in the data
- area of your application. Forms are roughly analogous to "blits" or
- "sprites" on other systems. (Note, however, that there is no sprite
- hardware on the ST.)
-
- Unlike other systems, there is NO predefined organization of the
- raster form. Instead, you determine the internal layout of the form
- with an auxiliary data structure called the \verb"MFDB", or Memory Form
- Definition Block. Before going into the details of the \verb"MFDB", we need
- to look at the various format options. Their distinguishing features
- are monochrome vs.~color, standard vs.~device-specific and even-word
- vs.~fringed.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Monochrome vs.~Color}
-
- Although these terms are standard, it might be better to say
- "single-color vs.~multi-color". What we are actually defining is the
- number of bits which correspond to each dot, or pixel, on the screen.
- In the ST, there are three possible answers. The high-resolution mode
- has one bit per pixel, because there is only one "color": white.
-
- In the medium resolution color mode, there are four possible
- colors for each pixel. Therefore, it takes two bits to represent
- each dot on the screen. (The actual colors which appear are
- determined by the settings of the ST's pallette registers.)
-
- In the low resolution color mode, sixteen colors are generated,
- requiring four bits per pixel. Notice that as the number of bits per
- pixel has been doubled for each mode, so the number of pixels on the
- screen has been halved: 640 by 400 for monochrome, 640 by 200 for
- medium-res, and 320 by 200 by low-res. In this way the ST always
- uses the same amount of video RAM: 32K.
-
- Now we have determined how many bits are needed for each pixel,
- but not how they are laid out within the form. To find this out, we
- have to see whether the form is device-dependent or not.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Standard vs.~Device-Specific Format}
-
- The standard raster form format is a constant layout which is
- the same for all GEM systems. A device-specific form is one which is
- stored in the internal format of a particular GEM system. Just as
- the ST has three different screen modes, so it has three different
- device-specific form formats. We will look at standard form first,
- then the ST-specific forms.
-
- First, it's reasonable to ask why a standard format is used. Its
- main function is to establish a portability method between various
- GEM systems. For instance, an icon created in standard format on an
- IBM PC GEM setup can be moved to the ST, or a GEM Paint picture from
- an AT\&T 6300 could be loaded into the ST version of Paint.
-
- The standard format has some uses even if you only work with the
- ST, because it gives a method of moving your application's icons and
- images amongst the three different screen modes. To be sure, there
- are limits to this. Since there are different numbers of pixels in
- the different modes, an icon built in the high-resolution mode will
- appear twice as large in low-res mode, and would appear oblong in
- medium-res. (You can see this effect in the ST Desktop's icons.)
- Also, colors defined in the lower resolutions will be useless in
- monochrome.
-
- The standard monochrome format uses a one-bit to represent
- black, and uses a zero for white. It is assumed that the form begins
- at the upper left of the raster area, and is written a word at a
- time left to right on each row, with the rows being output top to
- bottom. Within each word, the most significant bit is the left-most
- on the screen.
-
- The standard color form uses a storage method called "color
- planes". The high-order bits for all of the pixels are stored just as
- for monochrome, followed by the next-lowest bit in another contiguous
- block, and so on until all of the necessary color bits have been
- stored.
-
- For example, on a 16-color system, there would be four different
- planes. The color of the upper-leftmost bit in the form would be
- determined by concatenating the high-order bit in the first word of
- each plane of the form.
-
- The system dependent form for the ST's monochrome mode is very
- simple: it is identical to the standard form! This occurs because
- the ST uses a "reverse-video" setup in monochrome mode, with the
- background set to white.
-
- The video organization of the ST's color modes is more
- complicated. It uses an "interleaved plane" system to store the bits
- which make up a pixel. In the low-resolution mode, every four words
- define the values of 16 pixels. The high-order bits of the four
- words are merged to form the left-most pixel, followed by the next
- lower bit of each word, and so on. This method is called
- interleaving because the usually separate color planes described
- above have been shuffled together in memory.
-
- The organization of the ST's medium-resolution mode is similar
- to low-res, except the only two words are taken at a time. These are
- merged to create the two bits needed to address four colors.
-
- You should note that the actual color produced by a particular
- pixel value is NOT fixed. The ST uses a color remapping system
- called a palette. The pixel value in memory is used to address a
- hardware register in the palette which contains the actual RGB
- levels to be sent to the display. Programs may set the palette
- registers with BIOS calls, or the user may alter its settings with
- the Control Panel desk accessory. Generally, palette zero
- (background) is left as white, and the highest numbered palette is
- black.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Even-Word vs.~Fringes}
-
- A form always begins on a word boundary, and is always stored
- with an integral number of words per row. However, it is possible
- to use only a portion of the final word. This partial word is called
- a "fringe". If, for instance, you had a form 40 pixels wide, it
- would be stored with four words per row: three whole words, and one
- word with the eight pixel fringe in its upper byte.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{MFDBs}
-
- Now we can intelligently define the elements of the \verb"MFDB". Its
- exact C structure definition will be found in the listing. The
- {\tt fd\_nplanes}\/ entry determines the color scheme: a value of one is
- monochrome, more than one denotes a color form. If {\tt fd\_stand} is zero,
- then the form is device-specific, otherwise it is in standard format.
-
- The {\tt fd\_w}\/ and {\tt fd\_h}\/ fields contain the pixel width and height of
- the form respectively. {\tt fd\_wdwidth}\/ is the width of a row in words.
- If {\tt fd\_w}\/ is not exactly equal to sixteen times {\tt fd\_wdwidth}, then the
- form has a fringe.
-
- Finally, {\tt fd\_addr}\/ is the 32-bit memory address of the form
- itself. Zero is a special value for {\tt fd\_addr}. It denotes that this
- \verb"MFDB" is for the video memory itself. In this case, the VDI
- substitutes the actual address of the screen, and it ignores ALL of
- the other parameters. They are replaced with the size of the whole
- screen and number of planes in the current mode, and the form is (of
- course) in device-specific format.
-
- This implies that any \verb"MFDB" which points at the screen can only
- address the entire screen. This is not a problem, however, since the
- the VDI raster calls allow you to select a rectangular region within
- the form. (A note to advanced programmers: If this situation is
- annoying, you can retrieve the address of the ST's video area from
- low memory, add an appropriate offset, and substitute it into the
- \verb"MFDB" yourself to address a portion of the screen.)
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Let's operate}
-
- Now we can look at the VDI raster operations themselves. There
- are actually three: transform form, copy raster opaque, and copy
- raster transparent. Both copy raster functions can perform a variety
- of logic operatoins during the copy.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsubsection{Transform Form}
-
- The purpose of this operation is to change the format of a form:
- from standard to device-specific, or vice-versa. The calling
- sequence is:
-
- \begin{verbatim}
- vr_trnfm(vdi_handle, source, dest);
- \end{verbatim}
-
- where {\tt source}\/ and {\tt dest}\/ are each pointers to \verb"MFDBs". They ARE allowed
- to be the same. Transform form checks the {\tt fd\_stand}\/ flag in the
- source \verb"MFDB", toggles it and writes it into the destination \verb"MFDB" after
- rewriting the form itself. Note that transform form CANNOT change
- the number of color planes in a form: {\tt fd\_nplanes}\/ must be identical in
- the two \verb"MFDBs".
-
- If you are writing an application to run on the ST only, you
- will probably be able to avoid transform form entirely. Images and
- icons are stored within resources as standard forms, but since they
- are monochrome, they will work "as is" with the ST.
-
- If you may want to move your program or picture files to another
- GEM system, then you will need transform form. Screen images can be
- transformed to standard format and stored to disk. Another system
- with the same number of color planes could the read the files, and
- transform the image to ITS internal format with transform form.
-
- A GEM application which will be moved to other systems needs to
- contain code to transform the images and icons within its resource,
- since standard and device-specific formats will not always coincide.
-
- If you are in this situation, you will find several utilities in
- the listings which you can use to transform \verb"G_ICON" and \verb"G_IMAGE"
- objects. There is also a routine which may be used with \verb"map_tree()"
- from the last column in order to transform all of the images and
- icons in a resource tree at once.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsubsection{Copy Raster Opaque}
-
- This operation copies all or part of the source form into the
- destination form. Both the source and destination forms must be in
- device-specific form. Copy raster opaque is for moving information
- between "like" forms, that is, it can copy from monochrome to
- monochrome, or between color forms with the same number of planes.
- The calling format is:
-
- \begin{verbatim}
- vro_cpyfm(vdi_handle, mode, pxy, source, dest);
- \end{verbatim}
-
- As above, the source and dest parameters are pointers to \verb"MFDBs"
- (which in turn point to the actual forms). The two \verb"MFDBs" may point
- to memory areas which overlap. In this case, the VDI will perform
- the move in a non-destructive order. Mode determines how the pixel
- values in the source and destination areas will be combined. I will
- discuss it separately later on.
-
- The pxy parameter is a pointer to an eight-word integer array.
- This array defines the area within each form which will be affected.
- \verb"Pxy[0]" and \verb"pxy[1]" contain, respectively, the X and Y coordinates of
- the upper left corner of the source rectangle. These are given as
- positive pixel displacements from the upper left of the form.
- \verb"Pxy[2]" and \verb"pxy[3]" contain the X and Y displacements for the lower
- right of the source rectangle.
-
- \verb"Pxy[4]" through \verb"pxy[7]" contain the destination rectangle in the
- same format. Normally, the destination and source should be the same
- size. If not, the size given for the source rules, and the whole are
- is transferred beginning at the upper left given for the destination.
-
- This all sounds complex, but is quite simple in many cases.
- Consider an example where you want to move a 32 by 32 pixel area
- from one part of the display to another. You would need to allocate
- only one \verb"MFDB", with a zero in the {\tt fd\_addr}\/ field. The VDI will take
- care of counting color planes and so on. The upper left raster
- coordinates of the source and destination rectangles go into \verb"pxy[0]",
- \verb"pxy[1]" and \verb"pxy[4]", \verb"pxy[5]" respectively. You add 32 to each of these
- values and insert the results in the corresponding lower right
- entries, then make the copy call using the same \verb"MFDB" for both source
- and destination. The VDI takes care of any overlaps.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsubsection{Copy Raster Transparent}
-
- This operation is used for copying from a monochrome form to a
- color form. It is called transparent because it "writes through" to
- all of the color planes. Again, the forms need to be in
- device-specific form. The calling format is:
-
- \begin{verbatim}
- vrt_cpyfm(vdi_handle, mode, pxy, source, dest, color);
- \end{verbatim}
-
- All of the parameters are the same as copy opaque, except that
- color has been added. Color is a pointer to a two word integer
- array. \verb"Color[0]" contains the color index which will be used when a
- one appears in the source form, and \verb"color[1]" contains the index for
- use when a zero occurs.
-
- Incidentally, copy transparent is used by the AES to draw
- \verb"G_ICONs" and \verb"G_IMAGEs" onto the screen. This explains why you do not
- need to convert them to color forms yourself.
-
- (A note for advanced VDI programmers: The pxy parameter in both
- copy opaque and transparent may be given in normalized device
- coordinates (NDC) if the workstation associated with {\tt vdi\_handle}\/ was
- opened for NDC work.)
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{The Mode Parameter}
-
- The {\tt mode}\/ variable used in both of the copy functions is an
- integer with a value between zero and fifteen. It is used to select
- how the copy function will merge the pixel values of the source and
- destination forms. The complete table of functions is given in the
- listings. Since a number of these are of obscure or questionable
- usefulness, I will only discuss the most commonly used modes.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsubsection{Replace Mode}
-
- A mode of 3 results in a straight-forward copy: every
- destination pixel is replaced with the corresponding source form
- value.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsubsection{Erase Mode}
-
- A mode value of 4 will erase every destination pixel which
- corresponds to a one in the source form. (This mode corresponds to
- the "eraser" in a Paint program.) A mode value of 1 will erase every
- destination pixel which DOES NOT correspond to a one in the source.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsubsection{XOR Mode}
-
- A mode value of 6 will cause the destination pixel to be toggled
- if the corresponding source bit is a one. This operation is
- invertable, that is, executing it again will reverse the effects.
- For this reason it is often used for "software sprites" which must be
- shown and then removed from the screens. There are some problems
- with this in color operations, though - see below.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsubsection{Transparent Mode}
-
- Don't confuse this term with the copy transparent function
- itself. In this case it simply means that ONLY those destination
- pixels corresponding with ones in the source form will be modified
- by the operation. If a copy transparent is being performed, the
- value of \verb"color[0]" is substituted for each one bit in the source form.
- A mode value of 7 selects transparent mode.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsubsection{Reverse Transparent Mode}
-
- This is like transparent mode except that only those destination
- pixels corresponding to source ZEROS are modified. In a copy
- transparent, the value of \verb"color[1]" is substituted for each zero bit.
- Mode 13 selects reverse transparent.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{The problem of color}
-
- I have discussed the various modes as if they deal with one and
- zero pixel values only. This is exactly true when both forms are
- monochrome, but is more complex when one or both are color forms.
-
- When both forms are color, indicating that a copy opaque is
- being performed, then the color planes are combined bit-by-bit using
- the rule for that mode. That is, for each corresponding source and
- destination pixel, the VDI extracts the top order bits and processes
- them, then operates on the next lower bit, and so on, stuffing each
- bit back into the destination form as the copy progresses. For
- example, an XOR operation on pixels valued 7 and 10 would result in a
- pixel value of 13.
-
- In the case of a copy transparent, the situation is more
- complex. The source form consists of one plane, and the destination
- form has two or more. In order to match these up, the \verb"color[]" array
- is used. Whenever a one pixel is found, the value of \verb"color[0]" is
- extracted and used in the bit-by-bit merge process described in the
- last paragraph. When a zero is found, the value of \verb"color[1]" is
- merged into the destination form.
-
- As you can probably see, a raster copy using a mode which
- combines the source and destination can be quite complex when color
- planes are used! The situation is compounded on the ST, since the
- actual color values may be remapped by the palette at any time. In
- many cases, just using black and white in \verb"color[]" may achieve the
- effects you desire. If need to use full color, experimentation is
- the best guide to what looks good on the screen and what is garish
- or illegible.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Optimizing raster operations}
-
- Because the VDI raster functions are extremely generalized, they
- are also slower than hand-coded screen drivers which you might write
- for your own special cases. If you want to speed up your
- application's raster operations without writing assembly language
- drivers, the following hints will help you increase the VDI's
- performance.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsubsection{Avoid merged copies}
-
- These are copy modes, such as XOR, which require that words be
- read from the destination form. This extra memory access increases
- the running time by up to fifty percent.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsubsection{Move to corresponding pixels}
-
- The bit position within a word of the destination rectangle
- should correspond with the bit position of the source rectangle's
- left edge. For instance, if the source's left edge is one pixel in,
- then the destination's edge could be at one, seventeen, thirty-three,
- and so. Copies which do not obey this rule force the VDI to shift
- each word of the form as it is moved.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsubsection{Avoid fringes}
-
- Put the left edge of the source and destination rectangles on
- an even word boundary, and make their widths even multiples of
- sixteen. The VDI then does not have to load and modify partial words
- within the destination forms.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsubsection{Use another method}
-
- Sometimes a raster operation is not the fastest way to
- accomplish your task. For instance, filling a rectangle with zeros
- or ones may be accomplished by using raster copy modes zero and
- fifteen, but it is faster to use the VDI \verb"v_bar" function instead.
- Likewise, inverting an area on the screen may be done more quickly
- with \verb"v_bar" by using BLACK in XOR mode. Unfortunately, \verb"v_bar" cannot
- affect memory which is not in the video map, so these alternatives
- do not always work.
-
-
- \newpage
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \section{Menu Structures}
-
- In this chapter,
- I will be discussing GEM menu structures and how to use them
- in your application. You will find the demonstration file containing
- the code for this chapter in the listing {\tt GEMCL07.C}.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Menu Basics}
-
- In ST GEM, the menu consists of a bar
- across the top of the screen which displays several sub-menu
- titles. Touching one of the titles causes it to highlight,
- and an associated "drop-down" to be drawn directly below on
- the screen. This drop-down may be dismissed by moving to
- another title, or by clicking the mouse off of the drop-down.
-
-
- To make a selection, the mouse is moved over the
- drop-down. Each valid selection is highlighted when the
- mouse touches it. Clicking the mouse while over one of
- these selections picks that item. GEM then undraws the
- drop-down, and sends a message to your application giving the
- object number of the title bar entry, and the object number
- of the drop-down item which were selected by the user. The
- selected title entry is left highlighted while your code
- processes the request.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Menu Structures}
-
- The data structure which defines a GEM
- menu is (surprise!) an object tree, just like the dialogs and
- panels which we have discussed before. However, the
- operations of the GEM menu manager are quite different from
- those of the form manager, so the internal design of the menu
- tree has some curious constraints.
-
- The best way to understand these constraints is to look
- at an example. The first item in the listing is the object
- structure (only) of the menu tree from the GEM Doodle/Demo
- sample application.
-
- The ROOT of a menu tree is sized to fit the entire
- screen. To satisfy the visual hierarchy principle the screen
- is divided into two parts: \verb"THE BAR",
- containing the menu titles, and \verb"THE SCREEN", while contains
- the drop-downs when they are drawn. Each of these areas is
- defined by an object of the same name, which are the only two
- objects linked directly below the ROOT of a menu tree. You
- will notice an important implication of this structure: The
- menu titles and their associated drop-downs are stored in
- entirely different subtrees of the menu!
-
- While examining \verb"THE BAR" in the example listing, you may
- notice that its \verb"OB_HEIGHT" is very large (513). In
- hexadecimal this is 0x0201. This defines a height for \verb"THE BAR"
- of one character plus two pixels used for spacing. \verb"THE BAR"
- and its subtree are the only objects which are drawn on
- the screen in the menu's quiescent state.
-
- The only offspring object of \verb"THE BAR" is \verb"THE ACTIVE". This
- object defines the part of \verb"THE BAR" which is covered by menu
- titles. The screen rectangle belonging to \verb"THE ACTIVE" is used
- by the GEM screen manager when it waits for the mouse to
- enter an active menu title. Notice that \verb"THE ACTIVE" and its
- offspring also have \verb"OB_HEIGHTs" with pixel residues.
-
- The actual menu titles are linked left to right in order
- below \verb"THE ACTIVE". Their \verb"OB_Xs" and \verb"OB_WIDTHs"
- are arranged so that they completely cover \verb"THE ACTIVE".
- Normally, the title objects are typed \verb"G_TITLE", a special
- type which assures that the title bar margins are correctly drawn.
-
- \verb"THE SCREEN" is the parent object of the drop-down boxes
- themselves. They are linked left to right in an order
- identical with their titles, so that the menu manager can
- make the correct correspondence at run-time. The \verb"OB_X" of
- each drop-down is set so that it is positioned below its
- title on the screen.
-
- Notice that it is safe to overlap the drop-downs within a
- menu, since only one of them will be displayed at any time.
- There is one constraint on the boxes however: They must be
- no greater than a quarter screen in total size. This is the
- size of the off-screen blit buffer which is used by GEM to
- store the screen contents when the drop-down is drawn. If
- you exceed this size, not all the screen under the drop-down
- will be restored, or the ST may crash!
-
- The entries within a drop-down are usually \verb"G_STRINGs",
- which are optimized for drawing speed. The rectangles of
- these entries must completely cover the drop-down, or the
- entire drop-down will be inverted when the mouse touches an
- uncovered area! Techniques for using objects other than
- \verb"G_STRINGs" are discussed later on.
-
- The first title and its corresponding drop-down are
- special. The title name, by custom, is set to DESK. The
- drop-down must contain exactly eight \verb"G_STRING" objects. The
- first (again by custom) is the INFO entry, which usually
- leads to a dialog displaying author and copyright information
- for your application. The next is a separator string of
- dashes with the \verb"DISABLED" flag set. The following six objects
- are dummy strings which GEM fills in with the names of desk
- accessories when your menu is loaded.
-
- The purpose of this description of menu trees is to give
- you an understanding of what lies "behind the scenes" in the
- next section, which describes the run-time menu library
- calls. In practice, the Resource Construction Set provides
- "blank menus" which include all of the required elements, and
- it also enforces the constraints on internal structure. You
- only need to worry about these if you modify the menu tree
- "on-the-fly".
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Using the menu}
-
- Once you have loaded the application's
- resource, you can ask the AES to install your menu. You must
- first get the address of the menu tree within the resource
- using:
-
- \begin{verbatim}
- rsrc_gaddr(R_TREE, MENUTREE, &ad_menu);
- \end{verbatim}
-
- assuming that \verb"MENUTREE" is the name you gave the menu in the
- RCS, and that {\tt ad\_menu} is a LONG which will receive the
- address. Then you call the AES to establish the menu:
-
- \begin{verbatim}
- menu_bar(ad_menu, TRUE);
- \end{verbatim}
-
- At this point, the AES draws your menu bar on the screen and
- animates it when the user moves the mouse into the title
- area.
-
- The AES indicates that the user has made a menu selection
- by sending your application a message. The message type is
- \verb"MN_SELECTED", which will be stored in \verb"msg[0]", the first
- location in the message returned by \verb"evnt_multi()".
-
- The AES also stores the object number of the selected
- menu's title in \verb"msg[3]", and the object number of the
- selected menu item in \verb"msg[4]". Generally, your application
- will process menu messages with nested C switch statements.
- The outer switch will have one case for each menu title, and
- the inner switch statements will have a case for each entry
- within the selected menu. (This implies that you must give a
- name to each title and to each menu entry when you create the
- menu in the RCS.)
-
- After the user has made a menu selection, the AES leaves
- the title of the chosen menu in reverse video to indicate
- that your application is busy processing the message. When
- you done with whatever action is indicated, you need to
- return the title to a normal state. This is done with
-
- \begin{verbatim}
- menu_tnormal(ad_menu, msg[3], TRUE);
- \end{verbatim}
-
- (Remember that \verb"msg[3]" is the title's object number.)
-
- When your application is ready to terminate, it should
- delete its menu bar. Do this with the call:
-
- \begin{verbatim}
- menu_bar(ad_menu, FALSE);
- \end{verbatim}
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Getting fancy}
-
- The techniques above represent the bare
- minimum to handle menus. In most cases, however, you will
- want your menus to be more "intelligent" in displaying the
- user's options. For instance, you can prevent many user
- errors by disabling inappropriate choices, or you can save
- space on drop-downs by showing only one line for a toggle and
- altering its text or placing and removing a check mark when
- the state is changed. This section discusses these and other
- advanced techniques.
-
- It is a truism of user interface design that the best way
- to deal with an error is not to let it happen in the first
- place. It many cases, you can apply this principle to GEM
- menus by disabling choices which should not be used. If
- your application uses a "selection precedes action" type of
- interface, the type of object selected may give the
- information needed to do this. Alternately, the state of
- the underlying program may render certain menu choices
- illegal.
-
- GEM provides a call to disable and re-enable menu
- options. The call is:
-
- \begin{verbatim}
- menu_ienable(ad_menu, ENTRY, FALSE);
- \end{verbatim}
-
- to disable a selection. The entry will be grayed out when
- it is drawn, and will not invert under the mouse and will not
- be selected by the user. Substituting TRUE for FALSE
- re-enables the option. \verb"ENTRY" is the name of the object
- which is being affected, as assigned in the RCS.
-
- Note that \verb"menu_ienable()" will not normally affect the
- appearance or operation of menu TITLE entries. However,
- there is an undocumented feature which allows this. If \verb"ENTRY"
- is replaced by the object number of a title bar entry with
- its top bit set, then the entire associated drop-down will be
- disabled or re-enabled as requested, and the title's
- appearance will be changed. But, be warned that this feature
- did not work reliably in some early versions of GEM. Test it
- on your copy of ST GEM, and use it with caution when you
- cannot control the version under which your application may
- run.
-
- It is also possible to disable menu entries by directly
- altering the \verb"DISABLED" attribute within the \verb"OB_STATE" word.
- The routines \verb"enab_obj()" and \verb"disab_obj()" in the listing show
- how this is done. They are also used in \verb"set_menu()", which
- follows them immediately.
-
- \verb"Set_menu()" is a utility which is useful when you wish to
- simultaneously enable or disable many entries in the menu
- when the program's state changes or a new object is selected
- by the user. It is called with
-
- \begin{verbatim}
- set_menu(ad_menu, vector);
- \end{verbatim}
-
- where vector is a pointer to an array of WORDs. The first
- word of the array determines the default state of menu
- entries. If it is TRUE, then \verb"set_menu()" enables all entries
- in every drop-down of the menu tree, except that the DESK
- drop-down is unaffected. If it is FALSE, then every menu
- entry is disabled.
-
- The following entries in the array are the numbers of
- menu entries which are to be toggled to the reverse of the
- default state. This list is terminated by a zero entry.
-
- The advantage of \verb"set_menu()" is that it allows you to
- build a collection of menu state arrays, and associate one
- with each type of user-selected object, program state, and so
- on. Changing the status of the menu tree may then be
- accomplished with a single call.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Check, please?}
-
- One type of state indicator which may
- appear within a drop-down is a checkmark next to an entry.
- You can add the checkmark with the call:
-
- \begin{verbatim}
- menu_icheck(ad_menu, ENTRY, TRUE);
- \end{verbatim}
-
- and remove it by replacing the TRUE with FALSE. As above,
- \verb"ENTRY" is the name of the menu entry of interest. The
- checkmark appears inside the left boundary of the entry
- object, so leave some space for it.
-
- The \verb"menu_icheck()" call is actually changing the state of
- the \verb"CHECKED" flag within the entry object's \verb"OB_STATE" word. If
- necessary, you may alter the flag directly using \verb"do_obj()" and
- \verb"undo_obj()" from the example listing.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Now you see it, now you don't}
-
- You can also alter the
- text which appears in a particular menu entry (assuming that
- the entry is a \verb"G_STRING" object). The call
-
- \begin{verbatim}
- menu_text(ad_menu, ENTRY, ADDR(text));
- \end{verbatim}
-
- will substitute the null-terminated string pointed to by
- text for whatever is currently in \verb"ENTRY". Remember to make
- the drop-down wide enough to handle the largest text string
- which you may substitute. In the interests of speed,
- \verb"G_STRINGs" drawn within drop-downs are not clipped, so you may
- get garbage characters on the desktop if you do not size the
- drop-down properly!
-
- The \verb"menu_text()" call actually alters the \verb"OB_SPEC" field of
- the menu entry object to point to the string which you
- specify. Since the menu tree is a static data structure
- which may be directly accessed by the AES at any time, be
- sure that the string is also statically allocated and that it
- is not modified without first being delinked from the menu
- tree. Failure to do this may result in random crashes when
- the user accesses the drop-down!
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Lunch and dinner menus}
-
- Some applications may have such
- a wide range of operations that they need more than one menu
- bar at different times. There is no problem with having
- more than one menu tree in a resource, but the AES can only
- keep track of one at a time. Therefore, to switch menus you
- need to use
-
- \begin{verbatim}
- menu_bar(ad_menu1, FALSE);
- \end{verbatim}
-
- to release the first menu, then use
-
- \begin{verbatim}
- menu_bar(ad_menu2, TRUE);
- \end{verbatim}
-
- to load the second menu tree.
-
- Changing the entire menu is a drastic action. Out of
- consideration for your user, it should be associated with
- some equally obvious change in the application which has just
- been manually requested. An example might be changing from
- spreadsheet to data graphing mode in a multi-function
- program.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Do it yourself}
-
- In one of the next sections, I will discuss how
- to set up user-defined drawing objects. If you have already
- discovered them on your own, you can use them within a
- drop-down or as a title entry.
-
- If the user-defined object is within a drop-down, its
- associated drawing code will be called once when the
- drop-down is first drawn. It will then be called in
- "state-change" mode when the entry is highlighted (inverted).
- This allows you to use non-standard methods to show
- selection, such as outlines.
-
- If you try to insert a user-defined object within the
- menu title area, remember that the \verb"G_TITLE" object which you
- are replacing includes part of the dark margin of the bar.
- You will need to experiment with your object drawing code to
- replicate this effect.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Make pretty}
-
- There are a number of menu formatting
- conventions which have become standard practice. Using these
- gives your application a recognizable "look-and-feel" and
- helps users learn it. The following section reviews these
- conventions, and supplies a few hints and tricks to obtain a
- better appearance for you menus.
-
- The second drop-down is customarily used as the FILE
- menu. It contains options related to loading and saving the
- files used by the application, as well as entries for
- clearing the workspace and terminating the program.
-
- You should avoid crowding the menu bar. Leave a couple
- of spaces between each entry, and try not to use more than
- 70\% of the bar. Not only does this look better, but you will
- have space for longer words if you translate your application
- to a foreign language.
-
- Similarly, avoid cluttering menu drop-downs. Try to keep
- the number of options to no more than ten unless they are
- clearly related, such as colors. Separate off dissimilar
- entries with the standard disabled dashes line. (If you are
- using \verb"set_menu()", remember to consider the separators when
- setting up the state vectors.)
-
- If the number of options grows beyond this bound, it may
- be time to move them to a dialog box. If so, it is a
- convention to put three dots following each menu entry which
- leads to a dialog. Also, allow a margin on the menu entries.
- Two leading blanks and a minimum of one trailing blank is
- standard, and allows room for checkmarks if they are used.
-
- Dangerous menu options should be far away from common
- used entries, and are best separated with dashed lines. Such
- options should either lead to a confirming go/no-go alert, or
- should have associated "undo" options.
-
- After you have finished defining a menu drop-down with
- the RCS, be sure that its entries cover the entire box.
- Then use ctrl-click to select the drop-down itself, and SORT
- the entries top to bottom. This way the drop-down draws in
- smoothly top to bottom.
-
- Finally, it is possible to put entries other than
- \verb"G_STRINGs" into drop-downs. In the RCS, you will need to
- import them via the clipboard from the Dialog mode.
-
- Some non-string object, such as icons and images, will
- look odd when they are inverted under the mouse. There is a
- standard trick for dealing with this problem. Insert the
- icon or whatever in the drop-down first. Then get a \verb"G_IBOX"
- object and position and size it so that it covers the first
- object as well as the extra area you would like to be
- inverted.
-
- Edit the \verb"G_IBOX" to remove its border, and assign the
- entry name to it. Since the menu manager uses \verb"objc_find()",
- it will detect and invert this second object when the mouse
- moves into the drop-down.
- Finally, {\bf do not} sort a drop-down which has been set up this
- way!
-
-
- \newpage
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \section{An insider's AES tricks}
-
- This chapter of ST PRO GEM is
- devoted to exploring some of the little documented, but powerful,
- features of GEM. Like the authors of most complex systems, the
- GEM programmers left behind a set of "hooks", powerful features
- which would aid them in enhancing the system later. I am going to
- lay out a number of these methods which have served me well in
- making creative use of the AES. You will find that most of them
- concern the object and form libraries, since I was most involved
- in those parts of GEM. There are probably many more useful tricks
- waiting to be found in other parts of GEM, so if you happen onto
- one, please let me know in the Feedback! The listing for this
- chapter is {\tt GEMCL11.C}.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Powerful objects}
-
- The first four tricks all involve augmenting standard AES
- objects. This is a powerful technique for two reasons. First,
- you can take advantage of the regular AES object and form
- libraries to draw and handle most of your objects, so that your
- program need only process the exceptions. Second, you can use the
- RCS to copy the special objects into multiple dialogs or
- resources. These four tricks are Extended Object Types, User-
- defined Objects, \verb"TOUCHEXIT", and \verb"INDIRECT". Let's look at each of
- them in turn.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Extended object types}
-
- If you look at the AES Object Library documentation, you will
- notice that the values for the \verb"OB_TYPE" field in an object are all
- 32 or less. This means that a number of bits are unused in this
- field. In fact, the AES completely ignores the top byte of the
- \verb"OB_TYPE" field. In addition, the RCS ignores the top byte, but it
- also preserves its value when an object is read, written, or
- copied.
-
- This gives you one byte per object to use as you see fit.
- Since the processing of an object or dialog is (so far) in the
- hands of the AES, the best uses of Extended Types are in flagging
- methods for initializing an object or handling its value when the
- dialog completes.
-
- For example, you might have several dialogs containing
- editable numeric fields. The Extended Type of each numeric field
- could be set to the index of the corresponding value in an array.
- Then your application's dialog initialization code could scan the
- object tree for such objects, pick up the appropriate value
- from the array and convert it to ASCII, storing the result in the
- resource's string area. When the dialog was finished, another
- pass could be made to reconvert the ASCII to binary and store away
- the results in the array. (Note that the \verb"map_tree()" utility from
- a previous chapter will scan an entire resource tree.)
-
- Another application is to assign uniform codes to exit
- buttons in dialogs. If you give every "OK" button an Extended
- Type of one, and every "Cancel" button an Extended Type of two,
- then you needn't worry about naming every exit object. Just
- examine the Extended Type of the object returned by \verb"form_do", and
- proceed accordingly.
-
- The catch, of course, is that you have to find a way to enter
- the Extended Type code in the first place. Version 1.0 of the
- RCS, as shipped with the Atari developer's kit, makes no provision
- for this. So you have your choice of two methods for creating the
- first object with each Extended Type code.
-
- First, you can dump out a C source of a resource, insert the
- new type code by hand, and regenerate the resource with \verb"STCREATE".
- Alternately, you could carefully modify the binary resource using
- SID. You will probably want to reread the AES object manual and
- use the C source as a guide when doing so.
- In both cases, you should make things easy on yourself by creating
- a one dialog resource with only a single object other than the
- root. Version 2.0 of the RCS will let you directly enter an
- Extended Type, but it has not yet been released for the ST by DRI.
-
- Once you have created a prototype extended object by either
- method, you can use the RCS to propogate it. The safest way is to
- use the MERGE option to add the modified tree to the resource you
- are building. Then copy the prototype object via the clipboard to
- your dialog(s), deleting the extra tree when you are done. If you
- are using several different extended objects, you can use MERGE
- and clipboard copies to get them all into one tree which will then
- become your own object library.
-
- The second way of using RCS is easier, but more dangerous.
- If you want to try the following technique, BACK UP YOUR RCS DISK
- FIRST! Put simply, the RCS does not care what is in its dialog
- partbox. It will make copies of anything that it finds there! This
- gives you the option of using the RCS on ITS OWN RESOURCE in order
- to add your customized objects to the partbox.
-
- To do this, open RCS.RSC from the RCS. Since there is no DEF
- file, you will get a collection of question mark icons. Use the
- NAME option to make TREE5 into a DIALOG. Open it, and you will
- see the dialog partbox.
-
- Now you can use the MERGE technique described above to insert
- your customized objects. Then SAVE the modified resource, and
- rerun the RCS. Your new objects should now appear in the partbox.
- If you added several, you may have to stretch the partbox to see
- them all. You can now make copies of the new objects just like
- any other part. (Note: DO NOT modify the alert or menu partboxes,
- you will probably crash the RCS.)
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{User-defined objects}
-
- The one type of object which was not discussed in the earlier
- chapters on AES objects was \verb"G_USERDEF", the programmer defined
- object. This is the hook for creating objects with other
- appearances beyond those provided by the standard AES. By the
- way, you should note that the \verb"G_PROGDEF" and \verb"APPLBLK" mnemonics used
- in the AES documents are incorrect; the actual names as used
- defined \verb"OBDEFS.H" are \verb"G_USERDEF" and \verb"USERBLK".
-
- The RCS does not support the creation of \verb"G_USERDEF" objects,
- since it has no idea how they will be drawn by your program.
- Instead, you must insert a dummy object into your resource where
- you want the \verb"G_USERDEF" to appear, and patch it after your
- application performs its resource load.
-
- You must replace the object's existing \verb"OB_TYPE" with
- \verb"G_USERDEF", though you may still use the upper byte as an Extended
- Type. You must also change the \verb"OB_SPEC" field to be a 32-bit
- pointer to a \verb"USERBLK" structure. An \verb"USERBLK" is simply two LONG
- (32-bit) fields. The first is the address of the drawing code
- associated with the user defined object. The second is an
- arbitrary 32-bit value assigned to the object by your application.
-
- This one is useful if several objects have different \verb"USERBLK"s but
- call the same drawing code, because the code can detect who called it
- without without evaluating \verb"pb_tree" or \verb"pb_obj". When called,
- AES will pass the PARMBLK structure which also contains position and size of
- the object. Whenever the object needs to be redrawn the \verb"objc_draw"
- or \verb"objc_change" calls in turn call \verb"ub_code" as a subroutine
- of the AES. So the routine will be called in supervisor mode and all
- registers except for \verb"A0-A2/D0-D2" MUST be saved. Also \verb"ub_code"
- may NOT make any AES calls because GEM is not reentrant. VDI, XBIOS, BIOS
- and GEMDOS routines may be called however. \verb"ub_code" will only be called
- if the object lies within the clipping area. If \verb"ub_code" was called
- during an \verb"objc_change" call, the state of the object may just have
- changed. Then, parameters \verb"pb_prevstate" and \verb"pb_currstate"
- in the structure \verb"PARMBLK" contain different values.
-
- The object's status bits will be evaluated immediately after return
- of \verb"ub_code". These are not taken from the \verb"OBJECT" structure,
- but are returned by \verb"ub_code"! So it's sufficient to return
- \verb"pb_currstate" to make AES change the object properly.
-
- You can designate objects for conversion to \verb"G_USERDEFs" in the
- normal fashion by assigning them names which are referenced one by
- one in your initialization code. You can also combine two tricks
- by using the Extended Type field as a designator for objects to be
- converted to \verb"G_USERDEF". Each tree can then be scanned for objects
- to be converted. There is a short code segment in the listings
- which demonstrates this technique.
-
- My usual convention is to define new drawing objects as
- variants of existing objects, using the Extended Type field to
- designate the particular variation. Thus an Extended Type of one
- might designate a \verb"G_BUTTON" with rounded corners, while a value of
- two could flag a \verb"G_STRING" of boldface text. When using this
- technique, the RCS can be used to build a rough facsimile of the
- dialog by inserting the basic object type as placeholders. The
- existing \verb"OB_SPEC" information can then be copied to the second
- position in the \verb"USERBLK" when the object is initialized.
-
- One final note before moving on: There is no reason that the
- \verb"USERBLK" cannot be extended beyond two fields. You might want to
- add extra words to store more information related to drawing the
- object, such as its original type.
-
- The AES will call your drawing code whenever the \verb"G_USERDEF"
- needs to be drawn. This occurs when you make an \verb"objc_draw" call
- for its tree, or when an \verb"objc_change" occurs for that object. If
- your user-defined object is in a menu drop-drop, then your drawing
- code will be called any time the user exposes that menu.
-
- Before getting into the details of the AES to application
- calling sequence, some warnings are in order. First, remember
- that your drawing code will execute in the AES' context, using its
- stack. Therefore, be careful not to overuse the stack with deep
- recursion, long parameter lists, or large dynamic arrays. Second,
- the AES is NOT re-entrant, so you may not make ANY calls to it
- from within a \verb"G_USERDEF" procedure. You may, of course, call the
- VDI. Finally, realize that drawing code associated with a menu
- object may be called by the AES at any time. Exercise great care
- in sharing data space between such code and the rest of the
- application!
-
- When your drawing code is called by the AES, the stack is set
- up as if a normal procedure call had occured. There will be one
- parameter on the stack: a 32-bit pointer to a \verb"PARMBLK" structure.
- This structure lies in the AES' data space, so do not write beyond
- its end!
-
- The \verb"PARMBLK" contains 15 words. The first two are the long
- address of the object tree being drawn, and the third word is the
- number of the \verb"G_USERDEF" object. You may need these values if the
- same drawing code is used for more than one object at a time.
- Words four and five contain the previous and current \verb"OB_STATE"
- values of the object. If these values are different, your drawing
- code is being called in response to an \verb"objc_change" request.
- Otherwise, the active AES call is an \verb"objc_draw".
-
- Words six through nine contain the object's rectangle on the
- screen. Remember that you cannot call \verb"objc_offset" within the
- drawing code, so you will need these values! The next four words
- contain the clipping rectangle specified in the active \verb"objc_change"
- or \verb"objc_draw" call. You should set the VDI clip rectangle to this
- value before doing any output to the screen.
-
- The last two words in the \verb"PARMBLK" contain a copy of the extra
- 32-bit parameter from the object's \verb"USERBLK". If you have followed
- the method of copying an \verb"OB_SPEC" into this location, these words
- will be your pointer to a string, or \verb"BITBLK", or whatever.
-
- When your drawing routine is done, it should return a zero
- value to the AES. This is a "magic" value; anything else will
- stop the drawing operation.
-
- The listing contains a sample drawing routine which defines
- one extended drawing object, a rounded rectangle button. You can
- use this procedure as a starting point for your own User Defined
- objects.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Put anything you want on the Desktop!}
-
- In a previous chapter I described the use of the \verb"WF_NEWDESK"
- \verb"wind_set" call to substitute your own object tree for the normal
- green desktop background. If the tree you supply contains User
- Defined objects, you can draw whatever you want on the desktop!
- Some of the things you might try are free hand drawings imported
- in metafile format from EasyDraw, or whole screen bit images
- generated by Degas. If you do the latter, you will have to store
- the entire image off screen and blit parts of it to the display as
- requested.
-
- In any case, remember that your desktop drawing code can be
- called any time that a window is moved, so exercise the same care
- as with a menu drawer. Also, be aware that making the \verb"WF_NEWDESK"
- call does not force an immediate redraw of the desktop. To do
- that, do a \verb"form_dial(3)" call for the entire desktop rectangle.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{The Touchexit Flag}
-
- The \verb"TOUCHEXIT" attribute is an alternative to the more usual
- \verb"EXIT". When the \verb"TOUCHEXIT" bit is set in an object's \verb"OB_FLAG" word,
- the \verb"form_do" routine will exit immediately when the mouse button is
- pressed with the cursor over the object. Your code can
- immediately take control of the mouse and display, without waiting
- for the release of the button. This method is used for generating
- effects such as slider bars within otherwise normal dialogs.
-
- The easiest way to code a \verb"TOUCHEXIT" handler is to place a
- loop around the \verb"form_do" call. If the object number returned is
- \verb"TOUCHEXIT", then the animation procedure is called, followed by a
- resumption of the \verb"form_do" (WITHOUT recalling \verb"form_dial" or
- \verb"objc_draw"!). If the object returned is a normal \verb"EXIT", the dialog
- is complete and control flows to the cleanup code.
-
- There is one idiosyncrasy of \verb"TOUCHEXIT" which should be noted.
- When the AES "notices" that the mouse button has been pressed over
- a \verb"TOUCHEXIT", it immediately retests the button state. If it has
- already been released, it waits to see if a double click is
- performed. If so, the object number returned by \verb"form_do" will have
- its high bit set. If you don't care about double clicks, your
- code should mask off this flag. However, you may want to use the
- double click to denote some enhanced action. For example, the GEM
- file selector uses a double click on one of the file name objects
- to indicate a selection plus immediate exit.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{The Indirect Flag}
-
- If the \verb"INDIRECT" bit is set in an object's \verb"OB_STATE" word, the
- AES interprets the 32-bit \verb"OB_SPEC" field as a pointer to the memory
- location in which the {\em actual}\/ \verb"OB_SPEC" is to be found. Like User
- Defined objects, this capability is not supported by the RCS, so
- you have to set up the \verb"INDIRECT" bit and alter the \verb"OB_SPEC" at run
- time.
-
- The value of \verb"INDIRECT" is that you can use it to associate an
- AES object with other data or code. The general technique is to
- set up a table with a spare 32-bit location at its beginning.
- Then, when initializing the application's resource, you move the
- true \verb"OB_SPEC" into this location, set the \verb"INDIRECT" flag, and
- replace the \verb"OB_SPEC" field with a pointer to the table. The object
- behaves normally during drawing and form handling. However, if you
- receive its number from \verb"form_do" or \verb"objc_find", you have an
- immediate pointer to the associated table, without having to go
- through a lookup procedure.
-
- This technique works well in programs like the GEM Desktop.
- Each \verb"G_ICON" object is set up with \verb"INDIRECT". Its \verb"OB_SPEC" goes to
- the beginning of a data area defining the associated file. The
- blank location at the beginning of file table is filled up with
- the former \verb"OB_SPEC", which points to a \verb"ICONBLK".
-
- You can also combine \verb"INDIRECT" with \verb"TOUCHEXIT" when creating
- objects that must change when they are clicked by a user. For
- instance, a color selection box might be linked to a table
- containing the various \verb"OB_SPEC" values through which the program
- will cycle. Each time the user clicked on the box, the \verb"TOUCHEXIT"
- routine would advance the table pointer, copy the next value into
- the dummy \verb"OB_SPEC" location at the front of the table, and redraw
- the object in its new appearance.
-
- A programmer who wanted to follow a truly object-oriented
- "Smalltalkish" approach could use the \verb"INDIRECT" method to bind AES
- drawing object to tables of associated procedures or "methods".
- For instance, one procedure could be flagged for use when the user
- clicked on the object, one when the object was dragged, one for
- double-click, and so on. If the table structure was capable of
- indicating that the true method was stored in another table, a
- rudimentary form of class inheritance could be obtained.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Instant co-routines}
-
- We turn to the AES event and message system for this trick.
- While some languages like Modula 2 provide a method for
- implementing co-routines, there is no such capability in C.
- However, we can effectively fake it by using the AES event
- library.
-
- As already seen in an earlier column, an application can
- write a message to its own event queue using the \verb"appl_write" call.
- Usually, this is a redraw message, but there is nothing to prevent
- you from using this facility to send messages from one routine in
- your program to another. To set up co-routines using this method,
- they would be coded as separate procedures called from the
- application's main event loop.
-
- When one of the co-routines wanted to call the other, it
- would post a message containing the request and any associated
- parameters into the application's queue and then return. The main
- loop would find the message and make the appropriate call to the
- second co-routine. It it was necessary to then re-enter the first
- co-routine at the calling point, the original message could
- contain an imbedded reply message to be sent back when the request
- was complete. A simple switch structure could then be used to
- resume at the appropriate point.
-
- There are two potential problems in using this method. The
- first is the limited capacity of the application event queue. The
- queue contains eight entries. While the AES economizes this
- space by merging redraws and multiple events, it cannot merge
- messages. Because of this limit, you must be extremely careful
- when one message received has the potential to generate two or
- more messages sent. Unless this situation is carefully managed,
- you can get a sort of "cancer" which will overflow the queue and
- probably crash your application.
-
- The second danger involves race conditions. Message sent by
- the application are posted to the end of the queue. If other
- events have occurred, such as mouse clicks or keyboard presses,
- they will be seen and processed ahead of the application generated
- message. This implies that you cannot use this method if the
- program must complete its action before a new user generated event
- can be processed.
-
- \subsection{Deadlocks}
-
- One note on deadlocks: you may have noticed, that sometimes AES
- doesn't react to user input until the mouse button is clicked several
- times. This happens because the GEM is not a true multitasking system.
- The mouse interrupt writes the events into a circular buffer. This
- buffer can store 32 events. If AES is not called for a while, the
- buffer may overflow, resulting in the loss of events. If the event is
- releasing a mouse button, and it is lost, the program is still waiting
- for the button to be released. And the user is confused, because the
- system "hangs".
-
- Some sources recommend an \verb"evnt\_timer(0L)" that gives AES
- the chance to empty its event buffer. GEM 2.x provides a call \verb"appl\_yield"
- that has the same result, but needs less time. Fortunately this function
- is also available in Atari's GEM 1.x. In addition to the "magic" values
- \verb"0x73" and \verb"0xC8" for VDI and AES respectively, it's possible
- to assign the value \verb"\0xC9" to Register \verb"D0" before calling the
- GEM trap. That will cause the system to jump directly to the AES internal
- \verb"appl\_yield" routine. Using \verb"appl\_yield" instead of \verb"evnt\_timer"
- will save considerable amounts of time: 1000 calls to \verb"appl\_yield"
- take 1.4 seconds compared to 20 seconds for 1000 calls to \verb"evnt\_timer"!
- Here is an assembly language routine that calls \verb"appl\_yield":
-
- \begin{verbatim}
- appl_yield:
- move.w #$C9,d0
- trap #2
- rts
- \end{verbatim}
-
- This routine can be added to the GEM-library. It takes no parameter
- and it doesn't return anything. It just keeps the event dispatcher happy.
-
- \newpage
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \section{VDI Graphics: Lines and Solids}
-
- This chapter of ST PRO GEM will explore the fundamentals of
- VDI graphics output. We will take a look at the commands necessary to
- output simple graphics such as lines, squares and circles as well
- as more complex figures such as polygons. We will then
- take a first look at graphics text output, with an emphasis
- on ways to optimize its drawing speed. As usual, there is a
- demonstration program with this chapter under the name {\tt GEMCL09.C}.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{A bit of history}
-
- One of the reasons that the VDI can be confusing is that
- drawing anything at all, even a simple line, can involve setting
- around four different VDI parameters before making the draw call!
- (Given the state of the GEM documents, just FINDING them can be
- fun!) Looking backwards a bit sheds some light on why the VDI is
- structured this way, and also gives us a framework for organizing
- a discussion of graphics output.
-
- The GEM VDI closely follows the so-called GKS standard, which
- defines capabilities and calling sequences for a standardized
- graphic input/output system. GKS is itself an evolution from an
- early system called "Core". Both of these standards were born in
- the days when pen plotters, vectored graphics displays, and
- minicomputers were the latest items. So, if you wonder why
- setting the drawing pen color is a separate command, just think
- back a few years when it actually meant what it says! (The
- cynical may choose instead to ponder the benefits of
- standardization.)
-
- When doing VDI output, it helps if you pretend that the
- display screen really is a plotter or some other separate device,
- which has its own internal parameters which you can set up and
- read back. The class of VDI commands called Attribute Functions
- let you set the parameters. Output Functions cause the "device"
- to actually draw someone once it is configured. The Inquire
- Functions let you read back the parameters if necessary.
-
- There are two parameters which are relevant no matter what
- type of object you are trying to draw. They are the writing mode
- and the clipping rectangle. The writing mode is similar to that
- discussed in the column on raster operations. It determines what
- effect the figure you are drawing will have on data already on the
- screen. The writing mode is set with the call:
-
- \begin{verbatim}
- vswr_mode(vdi_handle, mode);
- \end{verbatim}
-
- ({\tt vdi\_handle}, here and below, is the handle obtained from
- \verb"graf_handle" at the beginning of the program. Mode is a word which
- may be one of:
-
- \begin{description}
- \item[1] Replace Mode
- \item[2] Transparent Mode
- \item[3] XOR mode
- \item[4] Reverse Transparent Mode
- \end{description}
-
- In replace mode, whatever is on the screen is overwritten.
- If you are writing characters, this means the background of each
- character cell will be erased.
-
- In transparent mode, only the pixels directly under the
- "positive" part of the image, that is, where 1-bits are being
- written, will be changed. When writing characters, the background
- of the cell will be left intact.
-
- In XOR mode, an exclusive or is performed between the screen
- contents and what is being written. The effect is to reverse the
- image under areas where a 1-bit occurs.
-
- Reverse transparent is like transparent, but with a "reverse
- color scheme". That is, only places where a 0-bit is to be
- put are changed to the current writing color. When you
- write characters in reverse transparent (over white), the effect
- is reverse video.
-
- The other common parameter is the clipping rectangle. It
- defines the area on the screen where the VDI is permitted to draw.
- Any output which would fall outside of this area is ignored; it is
- effectively a null operation. The clip rectangle is set with the
- call:
-
- \begin{verbatim}
- vs_clip(vdi_handle, flag, pxy);
- \end{verbatim}
-
- {\tt pxy} is a four-word array. \verb"Pxy[0]" and \verb"pxy[1]" are the X and Y
- screen coordinates, respectively, of one corner of your clipping
- rectangle. \verb"Pxy[2]" and \verb"pxy[3]" are the coordinates of the
- diagonally opposite corner of the rectangle. (When working with
- the AES, use of a \verb"GRECT" to define the clip is often more
- convenient. The routine \verb"set_clip()" in the listing does this.)
-
- {\tt flag} is set to TRUE if clipping is to be used. If you set it
- to FALSE, the entire screen is assumed to be fair game.
-
- Normally, you should walk the rectangle list for the current
- window to obtain your clipping rectangles.
- However, turning off the clip speeds up all output
- operations, particularly text. You may do this ONLY when you are
- absolutely certain that the figure you are drawing will not extend
- out of the top-most window, or out of a dialog.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{The line forms on the left}
-
- The VDI line drawing operations include polyline, arc,
- elliptical arc, and rounded rectangle. I'll first look at the
- Attribute Functions for line drawing, then go through the drawing
- primitives themselves.
-
- The most common used line attributes are color and width.
- The color is set with:
-
- \begin{verbatim}
- vsl_color(vdi_handle, color);
- \end{verbatim}
-
- where color is one of the standard VDI color indices, ranging
- from zero to 15. (The color which
- actually appears will depend on the pallette setting of your ST.)
-
- The line width may only be set to ODD positive values, for
- reasons of symmetry. If you try to set an even value, the VDI
- will take the next lower odd value. The call is:
-
- \begin{verbatim}
- vsl_width(vdi_handle, width);
- \end{verbatim}
-
- The two less used line parameters are the end style and
- pattern. With the end style you can cause the output line to
- have rounded ends or arrowhead ends. The call is:
-
- \begin{verbatim}
- vsl_ends(vdi_handle, begin_style, end_style);
- \end{verbatim}
-
- {\tt Begin\_style} and {\tt end\_style} are each words which may have the
- values zero for square ends (the default), one for arrowed ends,
- or two for rounded ends. They determine the styles for the
- starting and finishing ends of the line, respectively.
-
- The line pattern attribute can select dotted or dashed lines
- as well as more complicated patterns. Before continuing, you
- should note one warning: VDI line output DOES NOT compensate for
- pixel aspect ratio. That is, the dashes on a line will look twice
- as long drawn vertically on a medium-res ST screen as they do when
- drawn horizontally. The command for setting the pattern is:
-
- \begin{verbatim}
- vsl_type(vdi_handle, style);
- \end{verbatim}
-
- Style is a word with a value between 1 and 7. The styles
- selected are:
-
- \begin{description}
- \item[1] Solid (the default)
- \item[2] Long Dash
- \item[3] Dot
- \item[4] Dash, Dot
- \item[5] Dash
- \item[6] Dash, Dot, Dot
- \item[7] (User defined style)
- \end{description}
-
- The user defined style is determined by a 16-bit pattern
- supplied by the application. A one bit in the pattern turns a
- pixel on, a zero bit leaves it off. The pattern is cycled through
- repeatedly, using the high bit first. To use a custom style, you
- must make the call:
-
- \begin{verbatim}
- vsl_udsty(vdi_handle, pattern);
- \end{verbatim}
-
- before doing \verb"vsl_type()".
-
- As I mentioned above, the line type Output Functions
- available are polyline, circular and ellliptical arc, and rounded
- rectangle. Each has its own calling sequence. The call for a
- polyline is:
-
- \begin{verbatim}
- v_pline(vdi_handle, points, pxy);
- \end{verbatim}
-
- {\tt points} tells how many vertices will appear on the polyline. For
- instance, a straight line has two vertices: the end and the
- beginning. A closed square would have five, with the first and
- last identical. (There is no requirement that the figure
- described be closed.)
-
- The {\tt pxy} array contains the X and Y raster coordinates for the
- vertices, with a total of 2 * points entries. \verb"Pxy[0]" and \verb"pxy[1]"
- are the first X-Y pair, and so on.
-
- If you happen to be using the XOR drawing mode, remember that
- drawing twice at a point is equivalent to no drawing at all.
- Therefore, for a figure to appear closed in XOR mode, the final
- stroke should actually stop one pixel short of the origin of the
- figure.
-
- You may notice that in the GEM VDI manual the rounded
- rectangle and arc commands are referred to as GDPs (Generalized
- Drawing Primitives). This denotation is historical in nature, and
- has no effect unless you are writing your own VDI bindings.
-
- The rounded rectangle is nice to use for customized buttons
- in windows and dialogs. It gives a "softer" look to the screen
- than the standard square objects. The drawing command is:
-
- \begin{verbatim}
- v_rbox(vdi_handle, pxy);
- \end{verbatim}
-
- {\tt pxy} is a four word array giving opposite corners of the
- rectangle, just as for the \verb"vs_clip()" call. The corner rounding
- occurs within the confines of this rectangle. Nothing will
- protrude unless you specify a line thickness greater than one.
- The corner rounding is approximately circular; there is no user
- control over the degree or shape of rounding.
-
- Both the arc and elliptical arc commands use a curious method
- of specifying angles. The units are tenths of degrees, so an
- entire circle is 3600 units. The count starts at ninety degrees
- right of vertical, and proceeds counterclockwise. This means that
- "3 o'clock" is 0 units, "noon" is 900 units, "9 o'clock" is 1800
- units, and 2700 units is at "half-past". 3600 units take you back
- to "3 o'clock".
-
- The command for drawing a circular arc is:
-
- \begin{verbatim}
- v_arc(vdi_handle, x, y, radius, begin, end);
- \end{verbatim}
-
- {\tt x} and {\tt y} specify the raster coordinates of the center of the
- circle. {\tt radius} specifies the distance from center to all points
- on the arc. {\tt begin} and {\tt end} are angles given in units as described
- above, both with values between 0 and 3600. The drawing of the
- arc ALWAYS proceeds counterclockwise, in the direction of
- increasing arc number. So values of 0 and 900 for begin and end
- would draw a quarter circle from "three o'clock" to "noon".
- Reversing the values would draw the other three quarters of the
- circle.
-
- A \verb"v_arc()" command which specifies a "full turn" is the
- fastest way to draw a complete circle on the screen. Be warned,
- however, that the circle drawing algorithm used in the VDI seems
- to have some serious shortcomings at small radii! You can
- experiment with the CIRCLE primitive in ST Logo, which uses
- \verb"v_arc()", to see what I mean.
-
- Notice that if you want an arc to strike one or more given
- points on the screen, then you are in for some trigonometry. If
- your math is a bit rusty, I highly recommend the book "A
- Programmer's Geometry", by Bowyer and Woodwark, published by
- Butterworths (London, Boston, Toronto).
-
- Finally, the elliptical arc is generated with:
-
- \begin{verbatim}
- v_ellarc(vdi_handle, x, y, xrad, yrad, begin, end);
- \end{verbatim}
-
- {\tt y}, {\tt y}, {\tt begin}, and {\tt end} are just as before.
- {\tt xrad}\/ and {\tt yrad} give the
- horizontal and vertical radii of the defining ellipse. This means
- that the distance of the arc from center will be {\tt yrad} pixels at
- "noon" and "half-past", and it will be {\tt xrad} pixels at "3 and 9
- o'clock". Again, the arc is always drawn counterclockwise.
-
- There are a number of approaches to keeping the VDI's
- attributes "in sync" with the actual output operations. Probably
- the LEAST efficient is to use the Inquire Functions to determine
- the current attributes. For this reason, I have omitted a
- discussion of these calls from this chapter.
-
- Another idea is to keep a local copy of all significant
- attributes, use a test-before-set method to minimize the number of
- Attribute Functions which need to be called. This puts a burden
- on the programmer to be sure that the local attribute variables
- are correctly maintained. Failure to do so may result in obscure
- drawing bugs. If your application employs user defined AES
- objects, you must be very careful because GEM might call your draw
- code in the middle of a VDI operation (particularly if the user
- defined objects are in the menu).
-
- Always setting the attributes is a simplistic method, but
- often proves most effective. The routines \verb"pl_perim()" and
- \verb"rr_perim()" in the listing exhibit this approach. Modification
- for other primitives is straightforward. This style is most
- useful when drawing operations are scattered throughout the
- program, so that keeping track of the current attribute status is
- difficult. Although inherently inefficient, the difference is not
- very noticable if the drawing operation requested is itself time
- consuming.
-
- In many applications, such as data graphing programs or
- "Draw" packages, the output operations are centralized, forming
- the primary functionality of the code. In this case, it is both
- easy and efficient to keep track of attribute status between
- successive drawing operations.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Solids}
-
- There are a wider variety of VDI calls for drawing solid
- figures. They include rectangle or bar, disk, pie, ellipse,
- elliptical pie, filled rounded rectangle, and filled polygonal
- area. Of course, filled figure calls also have their own set of
- attributes which you will need to set.
-
- The fill color index determines what pen color will be used
- to draw the solid. It is set with:
-
- \begin{verbatim}
- vsf_color(vdi_handle, color);
- \end{verbatim}
-
- {\tt color} is just the same as for line drawing. A solid may or
- may not have a visible border. This is determined with the call:
-
- \begin{verbatim}
- vsf_perimeter(vdi_handle, vis);
- \end{verbatim}
-
- {\tt vis} is a Boolean. If it is true, the figure will be given a
- solid one pixel outline in the current fill color index. This is
- often useful to improve the appearance of solids drawn with a
- dithered fill pattern. If {\tt vis}\/ is false, then no outline is drawn.
-
- There are two parameters which together determine the pattern
- used to fill your figure. They are called interior style and
- interior index. The style determines the general type of fill,
- and the index is used to select a particular pattern if necessary.
- The style is set with the command:
-
- \begin{verbatim}
- vsf_interior(vdi_handle, style);
- \end{verbatim}
-
- where {\tt style}\/ is a value from zero through four. Zero selects a
- hollow style: the fill is performed in color zero, which is
- usually white. Style one selects a solid fill with the current
- fill color. A style of two is called "pattern" and a three is
- called "hatch", which are terms somewhat suggestive of the options
- which can then be selected using the interior index. Style four
- selects the user defined pattern, which is described below.
-
- The interior index is only significant for styles two and
- three. To set it, use:
-
- \begin{verbatim}
- vsf_style(vdi_handle, index);
- \end{verbatim}
-
- (Be careful here: it is very easy to confuse this call with the
- one above due to the unfortunate choice of name.) The {\tt index}\/
- selects the actual drawing pattern. The GEM VDI manual shows fill
- patterns corresponding to index values from 1 to 24 under style 2,
- and from 1 to 12 under style 3. However, some of these are
- implemented differently on the ST. Rather than try to describe
- them all here, I would suggest that you experiment. You can do so
- easily in ST Logo by opening the Graphics Settings dialog and
- playing with the style and index values there.
-
- The user defined style gives you some interesting options for
- multi-color fills. It is set with:
-
- \begin{verbatim}
- vsf_udpat(vdi_handle, pattern, planes);
- \end{verbatim}
-
- {\tt planes} determines the number of color planes in the pattern
- which you supply. It is set to one if you are setting a
- monochrome pattern. (Remember, monochrome is not necessarily
- black). It may be set to higher values on color systems: two for
- ST medium-res mode, or four for low-res mode. If you use a number
- lower than four under low-res, the other planes are zero filled.
-
- The {\tt pattern} parameter is an array of words which is a
- multiple of 16 words long. The pattern determined is 16 by 16
- pixels, with each word forming one row of the pattern. The rows
- are arranged top to bottom, with the most significant bit to the
- left. If you have selected a multi-plane pattern, the entire
- first plane is stored, then the second, and so on.
-
- Note that to use a multi-plane pattern, you set the writing
- mode to replace using \verb"vswr_mode()". Since the each plane can be
- different, you can produce multi-colored patterns. If you use a
- writing color other than black, some of the planes may
- "disappear".
-
- Most of the solids Output Functions have analogous line
- drawing commands. The polyline command corresponds to the filled
- area primitive. The filled area routine is:
-
- \begin{verbatim}
- v_fillarea(vdi_handle, count, pxy);
- \end{verbatim}
-
- {\tt count} and {\tt pxy} are just the same as for \verb"v_pline()". If the
- polygon defined by \verb"pxy" is not closed, then the VDI will force
- closure with a straight line from the last to the first point.
- The polygon may be concave or self-intersecting. If perimeter
- show is on, the area will be outlined.
-
- One note of caution is necessary for both \verb"v_fillarea()" and
- \verb"v_pline()". There is a limit on the number of points which may be
- stored in \verb"pxy[]". This limit occurs because the contents of \verb"pxy[]"
- are copied to the \verb"intin[]" binding array before the VDI is called.
- You can determine the maximum number of vertices by checking
- \verb"intout[14]" after using the extended inquire function \verb"vq_extnd()".
-
- For reasons unknown to this writer, there are TWO different
- filled rectangle commands in the VDI. The first is
-
- \begin{verbatim}
- vr_recfl(vdi_handle, pxy);
- \end{verbatim}
-
- {\tt Pxy} is a four word array defining two opposite corners of the
- rectangle, just as in \verb"vs_clip()". \verb"Vr_recfl()" uses the fill
- attribute settings, except that it NEVER draws a perimeter.
-
- The other rectangle routine is \verb"v_bar()", with exactly the same
- arguments as \verb"vr_recfl()". The only difference is that the
- perimeter setting IS respected. These two routines are the
- fastest way to produce a solid rectangle using the VDI. They may
- be used in XOR mode with a BLACK fill color to quickly invert an
- area of the screen. You can improve the speed even further by
- turning off the clip (if possible), and byte aligning the left and
- right edges of the rectangle.
-
- Separate commands are provided for solid circle and ellipse.
- The circle call is:
-
- \begin{verbatim}
- v_circle(vdi_handle, x, y, radius);
- \end{verbatim}
-
- and the ellipse command is:
-
- \begin{verbatim}
- v_ellipse(vdi_handle, x, y, xrad, yrad);
- \end{verbatim}
-
- All of the parameters are identical to those given above for
- \verb"v_arc()" and \verb"v_ellarc()". The solid analogue of an arc is a "pie
- slice". The VDI pie commands are:
-
- \begin{verbatim}
- v_pieslice(vdi_handle, x, y, radius, begin, end);
- \end{verbatim}
-
- for a slice from a circular pie, and
-
- \begin{verbatim}
- v_ellpie(vdi_handle, x, y, xrad, yrad, begin, end);
- \end{verbatim}
-
- for a slice from a "squashed" pie. Again, the parameters are
- identical to those in \verb"v_arc()" and \verb"v_ellarc()". The units and
- drawing order of angles are also the same. The final solids
- Output Function is:
-
- \begin{verbatim}
- v_rfbox(vdi_handle, pxy);
- \end{verbatim}
-
- which draws a filled rounded rectangle. The {\tt pxy}\/ array defines
- two two opposite corners of the bounding box, as shown for
- \verb"vs_clip()".
-
- The issues involved in correctly setting the VDI attributes
- for a fill operation are identical to those in drawing lines. For
- those who want to employ the "always set" method, I have again
- included two skeleton routines in the listing, which can be
- modified as desired.
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- \newpage
- \section{VDI Graphics: Text Output}
-
- We will now look at simple VDI text output, and ways to optimize
- its speed. To keep the size of this first discussion of text within
- reason, I am going to restrict it to use of the mono-spaced system
- font in its default size and orientation. Discussion of
- alternate and proportionally spaced fonts, baseline rotation, and
- character scaling will be a later chapter. Sample code is provided
- in {\tt GEMCL10.C}.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Definitions}
-
- This chapter makes use of some terminology
- which may be unfamiliar if you have not used digital typefaces. A
- mono-spaced font is one in which each character occupies an
- identically wide space on the screen. A proportional font has
- characters which occupy different widths. For instance, an 'l'
- would probably be narrower than a 'w'.
-
- Text may be "justified" right, left, or center. This means
- that the right character, left character, or center position of
- the text string is constrained to a given location. In common
- usage, a page of text is "ragged right" if its lines are left
- justified only. The text page is "fully justified", "justified"
- or (ambiguously) "right justified" if BOTH the left and right
- characters are contrained to fixed columns. Full justification is
- produced by inserting extra blank characters in the case of a
- mono-spaced font, or by adding extra pixel columns in the case of
- proportional output.
-
- A text character (in a monospaced font) is written inside a
- standard sized cell or box. Vertically, the cell extends from the
- "top line" down to the "bottom line". If there are one or more
- blank lines at the top or bottom, they are called "leading" and
- are used to separate lines of text. The characters themselves
- always fall between the "ascent line", which is the highest line
- reached by characters such as 'd' and 'l', and the "descent line",
- which is the lowest line in characters like 'q' and 'g'. Other
- locations of interest are the "half line", which is the top of
- characters like 'a' or 'n', and the "base line", which is the
- bottom of characters which do not have descenders.
-
- Before plunging into the Attribute Functions for text, you
- should note that the writing mode (\verb"vswr_mode") and clipping
- rectangle (\verb"vs_clip") attributes discussed earlier
- also pertain to text. Since much of the discussion of text
- optimization will center on these attributes, you may want to
- review them.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Text attributes}
-
- The writing color for graphics text is set
- with the command:
-
- \begin{verbatim}
- vst_color(vdi_handle, color);
- \end{verbatim}
-
- {\tt color} is a word value between 0 and 15 which
- designates the output color index. As discussed in previous
- columns, the actual color which appears is dependent on the
- current palette settings. In applications such as word and
- outline processors it is important that characters and their
- background provide good contrast to avoid eyestrain. In these
- situations, you may want to use the \verb"setPalette" and/or \verb"setColor"
- XBIOS functions to force the palette to a known state before
- starting the application.
-
- You can choose a variety of special output effects for your
- text with the call:
-
- \begin{verbatim}
- vst_effects(vdi_handle, effects);
- \end{verbatim}
-
- {\tt effects} is a single flag word, with the bits having the following
- significance:
-
- \begin{description}
- \item[0] Thicken
- \item[1] Lighten
- \item[2] Skew
- \item[3] Underline
- \item[4] Outline
- \item[5] Shadow
- \end{description}
-
- In each case, turning the bit on selects the effect. Otherwise,
- the effect is off. Any number of multiple effects may be
- selected, but the result may not always be pleasing or legible.
-
- The "thicken" effect widens the character strokes by one
- pixel, resulting in the appearance of boldface type. The
- "lighten" effect superimposes a half-tone dither on the character.
- This mode is useful for indicating non-selectable text items, but
- is not legible enough for other purposes.
-
- The skew effect shifts the rows of the character the right,
- with the greatest displacement at the top. This results in the
- appearance of italic text. You should be aware that the VDI does
- not compensate for this effect. This means that a skewed italic
- character which is immediately followed by a normal blank will be
- overstruck, and part of the top of the character will disappear.
- Likewise, a skewed character written to the left of an existing
- normal character will overstrike part of it. There is a related
- bug in the VDI clipping logic which may cause some parts of a
- skewed character not to be redrawn if they fall at the edge of a
- clipping rectangle, even though they should fall within the
- region.
-
- The outline effect produces output which is a one pixel
- "halo" around the normal character. The shadow effect attempts to
- create a "drop shadow" to the side of the character. These
- effects should be used very sparingly with default sized fonts.
- They often result in illegible output.
-
- When graphics text is written, a screen coordinate must be
- specified for the output. The relationship of the text to the
- screen point is determined by the call:
-
- \begin{verbatim}
- vst_alignment(vdi_handle, hin, vin, &hout, &vout);
- \end{verbatim}
-
- {\tt hin} and {\tt vin} are each words, with values specifying the desired
- horizontal and vertical alignment, respectively. {\tt hout}\/ and
- {\tt vout}
- receive the actual values set by the VDI. If they differ from the
- requested values, an error has occurred.
-
- {\tt in} may be set to zero for left justification, one for center
- justification, or two for right justification. The coordinate
- given when text is written becomes the "anchor point" as described
- in the definitions above. The default justification is left.
-
- {\tt vin} determines what reference line of the text is positioned
- at the output coordinate. The selection values are:
-
- \begin{description}
- \item[0] baseline (default)
- \item[1] half line
- \item[2] ascent line
- \item[3] bottom line
- \item[4] descent line
- \item[5] top line
- \end{description}
-
- A common combination of alignments is left (0) and top line
- (5). This mode guarantees that all text output will lie to the
- right and below the output coordinate. This corresponds with the
- AES object and \verb"GRECT" coordinate systems.
-
- Finally, the call to do the actual output is:
-
- \begin{verbatim}
- v_gtext(vdi_handle, x, y, string);
- \end{verbatim}
-
- {\tt x} and {\tt y} define the screen coordinate to be used as the alignment
- point. {\tt string} is a pointer to a null terminated string, which
- must be total eighty characters or less, exclusive of the null.
- This limit is imposed by the size of the \verb"intin[]" array in the VDI
- binding. Be warned that it is NOT checked in the standard
- binding! Exceeding it may cause memory to be overwritten.
-
- One Inquire Function is useful with text output. The call
-
- \begin{verbatim}
- vqt_attributes(vdi_handle, attrib);
- \end{verbatim}
-
- reads back the current attribute settings into the 10 word array
- \verb"attrib[]". The main items of interest are \verb"attrib[6]" through
- \verb"attrib[9]", which contain the width and height of characters, and
- the width and height of the character cell in the current font.
- You should rely on this function to obtain size information,
- rather than using the output of the \verb"graf_handle()" function. On
- the ST, \verb"graf_handle()" always returns sizes for the monochrome mode
- system font, which will be incorrect in the color screen modes.
-
- \verb"Attrib[1]" will contain the current graphics text color as set
- by \verb"vst_color()". \verb"Attrib[3]" and \verb"[4]" contain the horizontal and
- vertical alignment settings, respectively. \verb"Attrib[5]" contains the
- current writing mode, as set by \verb"vswr_mode()".
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Optimization}
-
- The most common complaint about using bit maps
- for character output is lack of speed. This section suggests ways
- to speed things up. By adopting all of these methods, you can
- realize an improvement of two to three times in speed.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsubsection{Byte alignment}
-
- Since writing graphic text is essentially a
- bit-blit operation, characters which have "fringes", that is, do
- not align evenly with byte boundaries, will suffer performance
- penalities. The default system fonts in all resolutions of the ST
- are a multiple of eight pixels wide, so the problem reduces to
- assuring that each characters starts at a byte boundary in the
- screen bit map. This will be true if the horizontal pixel address
- of the left edge of the character is evenly divisible by eight.
-
- Obviously, byte alignment is easiest to enforce when the
- horizontal justification is right or left. Doing so with centered
- text is possible, but requires adding padding blanks to odd length
- strings.
-
- When writing text within windows, it is helpful to assure
- that the edges of the window working area are byte aligned. There
- is a section of code in the listing which shows a technique for
- converting a user requested window position and/or size to its
- working dimensions, byte-aligning the width and horizontal
- position, and computing the adjusted external window coordinates.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsubsection{Writing Mode}
-
- The fastest text output mode is replace. All
- other modes require reading in the target raster area and merging
- it with the new information. You may find that you must use
- transparent or reverse transparent mode, for instance, to use or
- preserve an underlying background color other than white. In this
- case, you can still do some optimization by filling in the
- background color for the entire string with a \verb"v_bar()" call, rather
- than doing it one character cell at a time.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsubsection{Clipping}
-
- VDI output always proceeds faster when the
- clipping rectangle is turned off, and text output is no exception.
- Remember that you may only do this if you are drawing into a
- dialog box, or into the interior of a window which you know is on
- top. (You can use the \verb"WM_TOPPED" and \verb"WM_NEWTOP" messages for
- keeping track of the top window, or use the \verb"WF_TOP wind_get()" call
- to find the current top.) In both of these cases, you will know
- the width of the drawing area, and you can truncate the output
- string to fit exactly, rather than setting the clipping rectangle.
- For this to work, you must have used the byte alignment technique
- to assure that the width of the writing area is a multiple of
- eight. The VDI will also run at the non-clipped speed if
- the ENTIRE string to be written is within the current clip
- rectangle. To compound the problem, there is a one-off bug in the
- detection algorithm for the right edge. That is, the clip
- rectangle has to be one pixel BEYOND the right edge of the text
- for the fast write to work.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsubsection{Bindings}
-
- The normal binding for \verb"v_gtext()" is inefficient.
- It copies the string which you supply character-by-character into
- \verb"intin[]" before it calls the VDI itself. In many cases, it will be
- more efficient for your application to place characters directly
- into \verb"intin[]" and make the VDI trap call directly. To give you a
- start, the code for the standard \verb"v_gtext()" binding has been
- included in the listing. When setting up \verb"intin[]", be sure not to
- load more than 80 characters, or you will probably crash the
- system!
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsubsection{Moving text}
-
- When performing text editing on the screen, you
- should avoid rewriting the string under edit whenever possible.
- It is always more efficient to use the raster operations to move a
- string to the right or left, assuming that you have obeyed the
- byte alignment rule. If you are deleting characters, blit the
- unchanged part of the screen to the left, and overstrike the last
- character in the string with a blank. If inserting characters,
- blit the trailing portion of the string to the right before
- writing in the new character.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{By the way$\ldots$}
-
- It turns out there is an undocumented feature in
- Alcyon C which allows you to imbed assembly code in-line. Try
- using:
-
- \begin{verbatim}
- asm(".....");
- \end{verbatim}
-
- where the dots are replaced with an assembly instruction. You get
- one instruction per \verb"asm()", one \verb"asm()" per line.
-
-
- \newpage
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \section{Computer - Human Interface}
-
- \centerline{\bf And now for something completely different!}
-
- This chapter of ST Professional GEM will be devoted
- to examining a few of the principles of computer/human interface
- design, or "religion" as some would have it. I'm going to start
- with basic ergonomic laws, and try to draw some conclusions which
- are fairly specific to designing for the ST.
-
- For those who did not pray for this topic, it seems fair to
- explain why your diet of hard-core technical information has been
- interrupted by a sermon! As a motivater, we might consider why
- some programs are said by reviewers to have a "hot" feel (and
- hence sell well!) while others are "confusing" or "boring".
-
- Alan Kay has said that "user interface is theatre". I think
- we may be able to take it further, and suggest that a successful
- program works a bit of magic, persuading the user to suspend his
- disbelief and enter an imaginary world behind the screen, whether
- it is the mathematical world of a spreadsheet, or the land of Pacman
- pursued by ghosts.
-
- A reader of a novel or science fiction story also suspends
- disbelief to participate in the work. Bad grammar and clumsy plotting
- by the author are jarring, and break down the illusion. Similarly,
- a programmer who fails to pay attention to making his interface
- fast and consistent will annoy the user, and distract him from
- whatever care has been lavished on the functional core of the program.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Credit where it's due}
-
- Before launching into the discussion
- of user interface, I should mention that the general treatment and
- many of the specific research results are drawn from Card, Newell,
- and Moran's landmark book on the topic, which is cited at the end
- of this chapter. Any errors in interpretation and application to
- GEM and the ST are entirely my own, however.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Fingertips}
-
- We'll start right at the user's fingers with the
- basic equation governing positioning of the mouse, Fitt's Law,
- which is given as
-
- \begin{equation}
- T = I \cdot \log_2( \frac{D}{S} + \frac{1}{2})
- \end{equation}
-
- where T is the amount of time to move to a target, D is the distance
- of the target from the current position, and S is the size of the
- target, stated in equivalent units. $\log_2$ is the base 2 (binary)
- logarithm function, and I is a proportionality constant, about
- 100 milliseconds per bit, which corresponds to the human's "clock
- rate" for making incremental movements.
-
- We can squeeze an amazing amount of information out of this
- formula when attempting to speed up an interface. Since motion time
- goes up with distance, we should arrange the screen with the
- usual working area near the center, so the mouse will have to move
- a smaller distance on average from a selected object to a menu or
- panel. Likewise, any items which are usually used together should
- be placed together.
-
- The most common operations will have the greater impact on
- speed, so they should be closest to the working area and perhaps
- larger than other icons or menu entries. If you want to have
- all other operations take about the same time, then the targets
- farthest from the working area should be larger, and those closer
- may be proportionately smaller.
-
- Consider also the implications for dialogs. Small check boxes
- are out. Large buttons which are easy to hit are in. There should
- be ample space between selectable items to allow for positioning
- error. Dangerous options should be widely separated from common
- selections.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Muscles}
-
- Anyone who has used the ST Desktop for any period
- of time has probably noticed that his fingers now know where to find
- the File menu. This phenomenon is sometimes called "muscle memory",
- and its rate of onset is given by the Power Law of Practice:
-
- \begin{equation}
- T(n) = T(1) \cdot {n^{-a}}
- \end{equation}
-
- where T(n) is the time on the nth trial, T(1) is the time on the
- first trial, and a is approximately 0.4.
-
- This first thing to note about the Power Law is that it only
- works if a target stays in the same place! This should be a potent
- argument against rearranging icons, menus, or dialogs without some
- explicit request by the user. The time to hit a target which moves
- around arbitrarily will always be T(1)!
-
- In many cases, the Power Law will also work for sequences of
- operations to even greater effect. If you are a touch typist, you
- can observe this effect by comparing how fast you can enter "the"
- in comparison to three random letters. We'll come back shortly
- to consider what we can do to encourage this phenomenon.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Eyes}
-
- Just as fingers are the way the user sends data to the
- computer, so the eyes are his channel from the machine. The rate
- at which information may be passed to the user is determined by
- the "cycle time" of his visual processor. Experimental results
- show that this time ranges between 50 and 200 milliseconds.
-
- Events separated by 50 milliseconds or less are always
- perceived as a single event. Those separated by more than 200
- milliseconds are always seen as separate. We can use these
- facts in optimizing user of the computer's power when driving the
- interface.
-
- Suppose your application's interface contains an icon which
- should be inverted when the mouse passes over it. We now know
- that flipping it within one twentieth of a second is necessary
- and sufficient. Therefore, if a "first cut" at the program achieves
- this performance, there is no need for further optimization, unless
- you want to interleave other operations. If it falls short, it will
- be necessary to do some assembly coding to achieve a smooth feel.
-
- On the other hand, two actions which you want to appear distinct
- or convey two different pieces of information must be separated
- by an absolute minimum of a fifth of a second, even assuming that
- they occur in an identical location on which the user's attention
- is already focused.
-
- We are able to influence the visual processing rate within the
- 50 to 200 millisecond range by changing the intensity of the stimulus
- presented. This can be done with color, by flashing a target, or
- by more subtle enhancements such as bold face type. For instance,
- most people using GEM soon become accustomed to the "paper white"
- background of most windows and dialogs. A dialog which uses a
- reverse color scheme, white letters on black, is visually shocking
- in its starkness, and will immediately draw the user's eyes.
-
- It should be quickly added that stimulus enhancement will only
- work when it unambiguously draws attention to the target. Three or
- four blinking objects scattered around the screen are confusing, and
- worse than no enhancement at all!
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Short-term memory}
-
- Both the information gathered by the eyes
- and movement commands on their way to the hand pass through short-term
- memory (also called working memory). The amount of information which
- can be held in short-term memory at any one time is limited. You can
- demonstrate this limit on yourself by attempting to type a sheet of
- random numbers by looking back and forth from the numbers to the
- screen. If you are like most people, you will be able to remember
- between five and nine numbers at a time. So universal is this
- finding that it is sometimes called "the magic number seven, plus
- or minus two".
-
- This short-term capacity sets a limit on the number of choices
- which the user can be expected to grasp at once. It suggests that
- the number of independent choices in a menu, for instance, should
- be around seven, and never exceed nine. If this limit is violated,
- then the user will have to take several glances, with pauses to
- think, in order to make a choice.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Chunking}
-
- The effective capacity of short-term memory can be
- increased when several related items are mentally grouped as a "chunk".
- Humans automatically adopt this strategy to save themselves time.
- For instance, random numbers had to be used instead of text in the
- example above, because people do not type their native language as
- individual characters. Instead, they combine the letters into words
- and remember these chunks instead. Put another way, the characters
- are no longer considered as individual choices.
-
- A well designed interface should promote the use of chunking
- as a strategy by the user. One easy way is to gather together
- related options in a single place. This is one reason that like
- commands are grouped into a single menu which is hidden except for
- its title. If all of the menu options were "in the open", the user
- would be overwhelmed with dozens of alternatives at once. Instead, a
- "Show Info" command, for instance, becomes two chunks: pick File
- menu, then pick Show.
-
- Sometimes the interface can accomplish the chunking for the user.
- Consider the difference between a slider bar in a GEM program, and
- a three digit entry field in a text mode application. Obviously,
- the GEM user has fewer decisions to make in order to set the associated
- variable.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Think!}
-
- While we are puttering around trying to speed up
- the keyboard, the mouse, and the screen, the user is actually
- trying to get some work done. We need to back off now, and
- look at the ways of thinking, or cognitive processes, that go into
- accomplishing the job.
-
- The user's goal may be to enter and edit a letter, to retrieve
- information from a database, or simply draw a picture, but it
- probably has very little to do with programming. In fact, the
- Problem Space Principle says that the task can be described as
- a set of states of knowledge, a set of operators and associated
- constraints for changing the states, and the knowledge to
- choose the appropriate operator, which resides in the user's head.
-
- Those with a background in systems theory can consider this
- as a somewhat abstract, but straightforward, statement in terms of
- state variables and operators. A programmer might compare the
- knowledge states to the values of variables, the operators to
- arithmetic and logic operations, the constraints to the rules of
- syntax, and the user's knowledge to the algorithm embodied by a
- program.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Are we not men?}
-
- A rational person will try to attain his
- goals (get the job done) by changing the state of his problem space
- from its initial state to the goal state. The initial state,
- for instance, might be a blank word processor screen. The desired
- final state is to have a completed business letter on the screen.
-
- The Rationality Principle says that the user's behavior in
- typing, mousing, and so on, can be explained by considering the
- tasks required to achieve the goal, the operators available to
- carry out the tasks, and the limitations on the user's knowledge,
- observations, and processing capacity. This sounds like the
- typical user of a computer program must spend a good deal of time
- scratching his head and wondering what to do next. In fact, one
- of Card and Moran's key results is that this is NOT what takes place.
-
- What happens, in fact, is that the trained user strikes a sort
- of "modus vivendi" with his tool and adopts a set of repetitive,
- trained behavior patterns as the best way to get the job done.
- He may go so far as to ignore some functions of the program in
- order to set up a reliable pattern. What we are looking for is a
- way of measuring and predicting the "quality" of this trained
- behavior. Since using computers is a human endeavor, we should
- consider not only the speed with which the task is completed, but
- the degree of annoyance or pleasure associated with the process.
-
- Card and Moran constructed a series of behavioral models which
- they called GOMS models, for Goals-Operators-Methods-Selection.
- These models suggested that in the training process the user
- learned to combine the basic operators in sequences (chunks!)
- which then became methods for reaching the goals. Then these
- first level methods might be combined again into second level
- methods, and so forth, as the learning progressed.
-
- The GOMS models were tested in a lengthy series of trials
- at Xerox PARC using a variety of word processing software. (Among
- the subjects of these experiments were the inventors of
- the windowing methods used in GEM!) The results were again
- surprising: the level of detail in the models was really unimportant!
-
- It turned out to be sufficient to merely count up the number
- of keystrokes, mouse movements, and thought intervals required
- by each task. After summing up all of the tasks, any extra time
- for the computer to respond, or the user to move his hands from
- keyboard to mouse, or eyes from screen to printed page is added in.
- This simplified version is called the Keystroke-Level Model.
-
- As an example of the Keystroke Model, consider the task of
- changing a mistyped letter on the screen of a GEM word processor.
- This might be broken down as follows:
-
- \begin{enumerate}
- \item find the letter on the screen;
- \item move hand to mouse;
- \item point to letter;
- \item click mouse button;
- \item move hand to keyboard;
- \item strike {\tt Delete} key;
- \item strike key for new character.
- \end{enumerate}
-
- The sufficiency of the Keystroke Model is great news for our
- attempt to design faster interfaces. It says we can concentrate
- our efforts on minimizing the number of total actions to be taken,
- and making sure that each action is as fast as possible. We have
- already discussed some ways to speed up the mouse and keyboard
- actions, so let's now consider how to speed up the thought intervals,
- and cut the number of actions.
-
- One way to cut down "think time" is to make sure that the
- capacity of short-term memory is not exceeded during the course of
- a task. For example, the fix-a-letter task described above required
- the user to remember:
-
- \begin{enumerate}
- \item his place in the overall job of typing the document;
- \item the task he is about to perform;
- \item where the bad character appeared, and
- \item what the new character was.
- \end{enumerate}
-
- When this total of items creeps toward seven, the user often loses his place
- and commits errors.
-
- You can appreciate the ubiquity of this problem by considering
- how many times you have made mistakes nesting parentheses,
- or had to go back to count them, because too many things happened
- while typing the line to remember the nesting levels. The moral is that
- operations with long strings of operands should be avoided when
- designing an interface.
-
- The single most important factor in making an interface
- comfortable to use is increasing its predictability, and
- decreasing the amount of indecision present at each step during
- a task. There is (inevitably) an Uncertainty Principle which
- relates the number of choices at each step to the associated
- time for thought:
-
- \begin{equation}
- T = I \cdot \log_2 ( N + 1 )
- \end{equation}
-
- where LOG2 is the binary logarithm function, N is the number of
- equally probable choices, and I is a constant of approximately
- 140 msec/bit. When the alternates are not equally probable, the
- function is more complex:
-
- \begin{equation}
- T = I \cdot \sum_{i=1}^N P(i) \cdot \log_2 ( \frac{1}{P(i)+1} )
- \end{equation}
-
- where the P(i) are the probabilities of each of the choices (which
- must sum to one). Those of you with some information theory
- background will recognize this formula as the entropy of
- the decision; we'll come back to that later.
-
- So what can we learn from this hash? It turns out, as we might
- expect, that we can decrease the decision time by making some
- of the user's choices more probable than others. We do that by
- means of feedback cues from the interface.
-
- The importance of reliable, continuous meaningful feedback
- cannot be emphasized enough. It helps the beginner learn the system,
- and its predictability makes the program comfortable for the expert.
- Programs with no feedback, or unreliable cues, produce confusion,
- dissonance, and frustration in the user.
-
- This principle is so important that I going to give several
- examples from common GEM practice. The Desktop provides several
- instances. When an object is selected and a menu drops down, only
- those choices which are legal for the object are in black. The
- others are dimmed to grey, and are therefore removed from the
- decision. When a pick is made from the menu, the bar entry remains
- black until the operation is complete, reassuring the user that
- the correct choice was made. In both the Desktop and the RCS,
- items which are double-clicked open up with a "zoom box" from
- the object, again showing that the right object was picked.
-
- Other techniques are useful when operator icons are exposed on
- the screen. When an object is picked, the legal operations might
- be outlined, or the bad choices might be dimmed. If the screen
- flashing produced by this is objectionable, the legal icons can
- be made mouse sensitive, so they will "light up" when the cursor
- passes over - again showing the user which choices are legal.
-
- The desire for feedback is so strong that it should be provided
- even while the computer is doing an operation on its own. The hour
- glass mouse form is a primitive example of this. More sophisticated
- are "progress indicators" such as animated thermometer bars,
- clocks, or text displays of the processing steps. The ST Desktop
- provides examples in the Format and Disk Copy functions. The purpose
- of all of these is to reassure the user that the operation is
- progressing normally. Their lack can lead to amusing spectacles
- such as secretaries leaning over to hear if their disk drives are
- working!
-
- Another commonly overlooked feature is error prevention and
- correction. Card and Moran's results showed that in order to go
- faster, people will tolerate error rates of up to 30% in their
- work. Any program which does not give a fast way to fix mistakes
- will be frustrating indeed!
-
- The best way to cope with an error is to "make it didn't happen",
- to quote a common child's phrase. The same feedback methods
- discussed above are also effective in preventing the user from
- picking inappropriate combinations of objects and operations.
- Replacement of numeric type-ins with sliders or other visual
- controls eliminates the common "Range Error". The use of radio
- buttons prevents the user from picking incompatible options.
- When such techniques are used consistently, the beginner also
- gains confidence that he may explore the program without blundering
- into errors.
-
- Once an error has occured, the best solution is to have an
- "inverse operation" immediately available. For instance, the way
- to fix a bad character is to hit the backspace key. If a line is
- inadvertantly deleted, there should be a way to restore it.
-
- Sometimes the mechanics of providing true inverses are
- impractical, or end up cluttering the interface themselves. In
- these cases, a global "Undo" command should be provided to
- reverse the effect of the last operation, no matter what it was.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Of modes and bandwidth}
-
- Now I am going to depart from
- the Card, Newell and Moran thread of discussion to consider
- how we can minimize the number of operations in a task by
- altering the modes of the interface. Although "no modes" has
- been a watchword of Macintosh developers, the term may need
- definition for Atarians.
-
- Simply stated, a mode exists any time you cannot get to
- all of the capabilities of the program without taking some
- intermediate step. Familiar examples are old-style "menu-driven"
- programs, in which user must make selections from a number of
- nested menus in order to perform any operation. The options
- of any one menu are unavailable from the others.
-
- Recall that the user is trying to accomplish work in his
- own problem space, by altering its states. A mode in the
- program adds additional states to the problem space, which he is
- forced to consider in order to get the job done. We might call
- an interface which is completely modeless "transparent", because it
- adds no states between the user and his work. One of the best
- examples of a transparent program is the 15-puzzle in the Macintosh
- desk accessory set. The problem space of rearranging the tiles
- is identical between the program and a physical puzzle.
-
- Unfortunately, most programmers find themselves forced to
- put modes of some sort into their programs. These often arise
- due to technological limitations, such as memory space, screen
- "real estate", or performance limitations of peripherals. The
- question is how the modes can be made least offensive.
-
- I will make the general claim that the frustration which a
- mode produces is directly proportional to the amount of the user's
- bandwidth which it consumes. In other words, we need to consider
- how many keystrokes, mouse clicks, eye movements, and so on, are
- going into manipulating the true problem states, and how many
- are being absorbed by the modes of the program. If the interface
- is wasting a large amount of the user's effort, it will be perceived
- as slow and annoying.
-
- Here we can consider again the hierarchy of goals and methods
- which the user employs. When the mode is low in the hierarchy,
- and close to the user's "fingertips", it is encountered the most
- frequently. For instance, consider how frustrating it would be
- to have to hit a function key before typing in each character!
-
- The "menu-driven" style of programs mentioned above are
- almost as bad, since usually only one piece of information is
- collected at each menu. Such a program becomes a labyrinth of
- states better suited to an adventure game!
-
- The least offensive modes are found at the higher, goal
- related levels of the hierarchy. The better they align with
- changes in the state of the original problem, the more they
- are tolerated. For example, a word processing program might
- have one screen layout for program editing, another for writing
- letters, and yet another while printing the documents. A
- multi-function business package might have one set of menus for
- the spreadsheet, another for a graphing module, and a third
- for a database.
-
- In some cases the problem solved by the program has convenient
- "fracture lines" which can be used to define the modes. An
- example in my own past is the RCS, where the editing of each
- type of resource tree forms its own mode, with each of the modes
- nested within the overall mode and problem of composing the
- entire resource tree.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{To do is to be!}
-
- Any narrative description of user interface
- is bound to be lacking. There is no way text can convey the vibrancy
- and tactile pleasure of a good interface, or the sullen boredom
- of a bad one. Therefore, I encourage you to experiment. Get out
- your favorite arcade game and see if you can spot some of the
- elements I have described. Dig into your slush pile for the most
- annoying program you have ever seen, run it and see if you can see
- mistakes. How would you fix them? Then... go do it to your own
- program!
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \newpage
- \section{More User Psychology}
-
- This chapter continues the discussion of user interface design. It begins
- where we left off, with a further treatment of the mode problem, and
- proceeds into topics such as visual grammar and layered interfaces.
-
- \subsection{Modes Again}
-
- If a program is modeless, it acts predictably, which turns out to be
- very important. On the other hand, a good definition for ``modes'' is hard
- to find. In the previous chapter I suggested that a mode exists when you cannot
- use all of the capabilities of the program without performing some
- intermediate step. If this is less than clear, here are two alternate
- definitions offering different views of the problem.
-
- The "TWO USER TEST". Consider the following thought experiment: Imagine
- that your ST (and GEM) had two mice, two cursors, and two users. Could
- they both effectively use the program at the same time? If so, the
- application is modeless. If there are points where one user can be
- ``locked out'' by the actions of the other, then a mode exists at that
- point. Let's consider some examples of this test.
-
- In any program which uses the GEM menu system, one user could stop the
- other by touching a menu hotspot and dropping a menu. This constitutes
- an inherent mode in the GEM architecture.
-
- On the GEM Desktop, two users could open windows and view files without
- interference. However, as soon as one person tries to delete a file
- (assuming the verify option is on), the other is brought to a halt as a
- dialog appears. Thus, we have found a modal dialog.
-
- In many "Paint-type" programs, such as MacPaint, PC Paint, and GEM
- Paint, two artists could co-exist quite well, utilizing the on-screen
- palette and tool selection. Of course, these programs also contain modal
- dialogs for such operations as file and brush shape selection. In
- contrast, consider the paint program DEGAS for the ST. Here, two artists
- could only work together as long as neither wanted to change tool or
- color. Then the display would have to be flipped to the selection
- screen, stopping the other user. This is a mode in the DEGAS interface.
-
- (By the way, this test is not just academic. The grand-daddy of all
- mouse based systems, NLS, demonstrated by Doug Englebart in 1968, had
- two mice and two users, one of whom was physically remote. Cooperative
- techniques such as this are still largely unexplored and unexploited.)
-
-
- \subsection{One Liner}
-
- Here's a terse definition by Jef Raskin: A program is modeless if a
- given action has one and only one result. Again, let's run a few
- examples.
-
- The menu dropdowns are clearly modal by this definition. Before the menu
- was activated, window control points could be activated with a click.
- However, when the dropdown is visible, a click action is interpreted as
- a menu selection or a dismissal of the dropdown. Similarly, dialogs are
- modal because the action of moving the mouse into the menu bar no longer
- causes the dropdown to appear.
-
- I am typing this using the First Word editor program. It has a nice
- desktop level box full of characters where I can click to get symbols
- which the ST keyboard won't produce. However, if I invoke the find or
- replace string dialog, the click-in-the-box action doesn't work anymore.
- This is a mode in the First Word interface.
-
- Finally, consider an "old style" menu program, the kind where you type
- in the number of the desired action from a list. Since the number "2"
- might mean "Insert the record" in one menu, and "Purge the file" in
- another, such a program is clearly modal by Raskin's definition.
-
- These three definitions say almost the same thing, but from different
- viewpoints. Depending on the situation, one or the other may be more
- intuitive for you. The goal of this type of analysis is to root out
- unnecessary modes, and to make sure that those which remain only appear
- when requested by the user, offer some visual cue such as a rubber line
- or standard dialog box, and are used consistently throughout the
- application.
-
-
- \subsection{Predictability Forever And Ever}
-
- As Raskin's definition makes clear, when the modes go away, the
- interface becomes predictable. Predictability leads to the formation of
- habits of use. Habits reduce "think time" and become progressively
- faster due to the Power Law of Practice I have discussed. This
- is exactly what we want!
-
- There is another benefit of predictability. A habit learned in one part
- of a program with a consistent interface can be transferred and used
- elsewhere in the application. If several programs share the same style
- of interface, the same habits can be used across a complete set of
- products. Learning time for the new functions becomes shorter, and the
- user is more likely to use the new feature.
-
-
- \subsection{It's A Bogeyman!}
-
- Most casual users are scared silly of computers and programs. (If you
- have any doubt, eavesdrop on a secretary with a new word processor, or
- the doctor's receptionist coping with an insurance data entry program.)
- In most cases, they have a right to be frightened. Even experienced
- programmers, prone to toss the manuals and hack away, know that moderate
- paranoia is the best way to deal with an unknown program. How must this
- feel to someone whose ability to perform (or lose) their job depends on
- an unpredictable (aha!) black box.
-
- So here's another way in which predictability works. But to produce a
- truly fearless user, we need other qualities as well. One is robustness,
- meaning that the program will not crash given normal or even bizarre
- actions by the user. Another is feedback, which shuts off invalid
- options, reinforces correct actions, and gives reassurance that an
- operation is proceeding normally. Finally, we need forgiveness, in the
- form of inverse operations or Undo options, when the inevitable mistake
- is made.
-
- The ultimate goal is make the program discoverable. This means the user
- should be able to safely "wing it" after a short session with the
- application and its interface. This practice ought to be considered the
- norm anyway, since the manual is always across the office or missing
- when an esoteric and half-forgotten feature is needed. If it is possible
- to muddle through such a situation by trial and error, without causing
- damage, the immediate problem will be solved, and the user will gain
- confidence.
-
-
- \subsection{Good Grammar Or...}
-
- So exactly what are these habits that are supposed to be so helpful? One
- of the most useful patterns is a consistent command grammar for the
- program. This may sound strange, since we have supposedly abandoned
- command line interfaces in the graphics world, but in fact, the same
- type of rules apply. For instance, in the world of \verb"A>" we might issue the
- command:
-
- \begin{verbatim}
- copy a:foobar.txt b:
- \end{verbatim}
-
- By analogy to Englist grammar, this command contains a verb, \verb"copy", a
- file as subject: \verb"a:foobar.txt", and a location as an object: \verb"b:". The
- equivalent GEM Desktop operation is:
-
- \begin{enumerate}
- \item Move mouse to {\tt foobar.txt} icon in {\tt a:} window
- \item Press mouse button
- \item Move mouse to {\tt b:} icon
- \item Release mouse button
- \end{enumerate}
-
- The operation can be described as a select-drag-drop sequence, with the
- select designating the subject file, the drag denoting the operation
- (copy), and the location of the drop showing the object. A grammar still
- exists, but its "terminal symbols" are composed of mouse actions
- interpreted in the context of the current screen display, rather than
- typed characters.
-
- One useful way to analyze simple grammars, including those used as
- command languages, is to separate them into prefix, postfix, and infix
- forms. In a prefix grammer, the operation to be performed precedes its
- operands, that is, its subject(s) and object(s). The DOS copy command
- given above is an example of a prefix command. LISP is an example of a
- language which uses prefix specification for its commands.
-
- Postfix grammars specify the action after all of the operands have been
- given. This command pattern is familiar to many as the way in which
- Hewlett-Packard calculators work. FORTH is an example of a language
- which uses a postfix grammar.
-
- Infix notation places the verb, or operator, between its subject and
- object. Conventional algebraic notation is infix, as are most computer
- languages such as C or PASCAL. The example GEM command given above is
- also infix, since the selection of a subject file preceded the action,
- which was followed by the designation of an object.
-
- The "standard" GEM command grammar, as used in the products produced by
- Digital Research, is in fact infix. This is not to say that GEM enforces
- such a convention, or that it is rigorously followed. However, when
- there is no pressing reason for a change, adoption of an infix command
- grammar will make your application feel most like others which users may
- have seen.
-
- The general problem of specifying a graphic command language can be
- difficult, but much of the problem has already been handled on the ST.
- Part of the solution is by constraint: the input and output hardware of
- the ST are predefined, so most developers will not need to worry about
- choosing a pointing device or screen resolution. The other part of the
- standard solution is the GEM convention for mouse usage. I am going to
- review these rules, and then describe of the situations in which they
- have been bent, and finally some alternate approaches which may prove
- useful to some developers.
-
- \subsection{Specifying A Subject}
-
- There are really two sets of methods for designating what is to be
- affected by an operation. One set is used when distinct objects are to
- be affected. Examples are file and disk icons in the Desktop and trees
- in the RCS. Another set of designation methods is used when continuous
- material, such as text or bit images, is being handled.
-
- When dealing with objects, a single mouse click (down and up) over the
- object selects it. The application should show that the selection has
- occurred by changing the appearance of the object. The most common
- methods are inverting the object, or drawing "handles" around it.
-
- Many operations allow "plural", or multiple object, selections. The GEM
- convention is that a click on an object while the shift key is held down
- extends the selection by adding that object. If the shift-clicked object
- was already selected, it is deleted from the selection list.
-
- Another way to select multiple objects is to use a "rubber box" to
- enclose them. This operation begins with drag on a part of the view
- where no object is present. The application then animates a rubber box
- on the screen as long as the mouse button is held down. When the button
- is released, all objects within the current extent of the box are
- selected. A shift-drag combination could be used to add the objects to
- an existing selection list.
-
- Selecting part of a text or bit plane display is also done with a rubber
- box. Since there are no "objects" in the view, any mouse drag is
- interpreted as the beginning of a selection operation. In the simplest
- case, a bit plane, the rectangle within the box when the button is
- released is the selected extent.
-
- When the underlying data has structure, such as words and lines of text,
- the display should reflect this fact during the selection operation.
- Typically, text selection is indicated by inversion of the characters
- rather than a rubber box. The selection extends along the starting line
- so long as the mouse stays within the line. If the mouse move off the
- starting text line, the implied selection is all characters between the
- starting character and the character currently under the mouse, which is
- not necessarily a rectangular area.
-
- An extended "plural" selection may be supported in text editing. The use
- of the shift key is also conventional in this application.
-
-
- \subsection{Action}
-
- With the subject designated, the user can now choose an operation. In
- many cases, this will be picked from the menu, in which case the entire
- command is complete. Some menu selections will lead to dialogs, in which
- the interaction methods are regulated by the GEM form manager. When the
- command is completed, it is often helpful if the application leaves the
- objects (or areas) selected and ready for another operation. A single
- click away from any object is interpreted as cancelling the selections.
-
- Many operations are indicated by gestures on the screen. Usually, this
- is some variant of a drag operation. The interpretation of the gesture
- may depend on the type and location of the selected subject, which part
- of it is under the mouse, and in what location the drag terminates.
-
- "Handles" are small boxes or dot displayed around an object when it is
- selected. A drag beginning with the mouse on a handle is usually
- interpreted as a resizing operation, if this is appropriate. The
- pointing finger mouse form is displayed to indicate the operation in
- progress, and a rubber version of the object is animated on the screen
- to show the user the result if the button were released. In some cases,
- where an underlying "snap" grid exists, the animated object may change
- size in discrete steps.
-
- Dragging a non-handle area of a selected object is usually interpreted
- as the beginning of a move function. In most applications, a move of a
- single object may be started without pre-selection. Simply beginning the
- drag on the object is taken to imply selection. The spread hand, or
- "grabber", mouse form is typically displayed during a drag operation.
-
- Dragging may denote copying or movement, or more complex functions such
- as instantiation or generalization. The operation implied by movement on
- the screen will differ among applications, and often within the same
- application, depending on target location. This target is the recipient
- of the command's action, or its object, in an English grammar sense.
-
- For example, a drag from window to window in the Desktop denotes a copy.
- On the other hand, dragging the same icon to the trashcan deletes it
- completely. Dragging an object from the RCS partbox to the editing view
- creates a new copy of that prototype object. Dragging the same object
- within the edit view simply changes its placement.
-
- There are some mouse actions which are conventional "abbreviations". A
- double click on an object is interpreted as both a selection and an
- action. Usually, the double click action is the same as the Open entry
- in the "File" menu.
-
- When the usual interpretation of a drag is movement, then shift-drag may
- be used as an enhanced varient implying copying. For instance, shift-
- dragging an object within the RCS editing window makes a copy of the
- object and places it in the final location.
-
- To return to the beginning of this discussion, the reason for adopting
- these conventional usages is to build an interface that promotes habits.
- Particularly, a standard grammar for giving commands helps answer the
- question "What comes next?". It breaks the user's actions into logical
- phrases, or chunks, which may be thought of a whole, rather than one
- action at a time.
-
- \subsection{Different Folks, Different Strokes}
-
- There are always exceptions to a rule, or so it seems. In this case,
- consistency of the interface grammar is sometimes traded off against
- consistency of metaphor, preservation of screen space, and "fast path"
- methods for experts.
-
- One example is the use of "tools" in Paint and Draw programs. In such
- programs, an initial click is made on a tool icon, denoting the
- operation to be applied to all following selections. This is an prefix
- style of grammar, and stands in contrast to the usual method of
- selecting subject object(s) first. Because of this contrast, it is
- sometimes called "moding the cursor". (Try applying the tests above to
- be sure it really is a mode.)
-
- In these cases, there are two reasons for accepting the nonstandard
- method. The first is consistency of metaphor. The "user model" portrayed
- in the programs is an artist's work table, with tools, palette, and so
- on. The cursor moding action is equivalent to picking up a working tool.
- The second reason is speed. In a Paint program, the "canvas" is often
- modified, and speed in creating or changing the bits is important. In
- more object oriented applications such as Desktop or RCS, the objects
- are more persistent. Speed is then more essential when adding or
- changing properties of the objects.
-
- When command styles are mixed in this fashion, you must design very
- carefully to avoid conflicts or apparent side-effects in the command
- language. For example, in GEM Draw picking an action from the Edit menu
- cancels the current cursor mode without warning. Confusion from such
- side-effects may cancel out the benefits of the mixed grammar.
-
- The subject of command further attention. While the novice approaching a
- program needs full feedback, a person who uses it day in and day out
- will learn the program, and want faster ways to get the job done, even
- if they are more arcane. The gives rise to a "layered" style of
- interface.
-
- A layered interface is designed so that the visual grammar is obvious,
- as we have discussed. However, there are one or more sets of
- "accelerators" built into the program, which may be harder to find but
- faster to use. One example is condensed mouse actions such as the
- double-click. For instance, attempting to select a block of text which
- extends beyond a window is impossible using the basic metaphor. The
- novice will simply do the operation in pieces. A layered interface might
- put a less obvious Mark Begin and Mark End option in the menus. Another
- way is to take a drag which extends outside the window as a request to
- begin scrolling in that direction, while extending the current
- selection.
-
- One of the most common and useful accelerator methods is function keys.
- Using this approach, single key equivalents to actions are listed in the
- menu. Striking this key when an object is selected will cause the action
- to occur. Note that this is most useful if some keyboard driven method
- of object selection, such as tabbing, is also available. Otherwise, the
- time switching from the mouse, used to select the object, to the
- keyboard for command input, may well cancel any advantage.
-
- Finally, radical departures from the GEM metaphor may be useful when
- attempting to replicate the look of another system, or trying to meet
- severe constraints, such as display space. One example would be
- discarding the standard GEM menus in favor of "popup" menus which appear
- next to the current mouse position in response to a click on the second
- button. This method has the advantage of preserving the menu space at
- the top of the screen, and is potentially faster because the menu
- appears right next to the current mouse position. The drawbacks are lack
- of a visual cue for naive users trying to find the commands, and the
- need for custom coding to build the popups.
-
-
- \subsection{More To Come}
-
- We have reached the end of the second sermon on user interface. In a
- future chapter, I will look at "higher level" topics relating to the
- design of the application's user metaphor. These include issues of
- object orientation, direct manipulation, and the construction of
- microworlds. In the meantime, several of the more practical columns will
- present implementions of techniques such as accelarator keys and popup
- menus which I have discussed this time.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{References}
-
- Stuart K.~Card, Thomas P.~Moran, and Allen Newell:
- {\em The Psychology of Human-Computer Interaction}, Lawrence
- Erlbaum Associates, Hillsdale, New Jersey, 1983. (Fundamental
- and indispensible. The volume of experimental results make it
- weighty. The Good Parts are at the beginning and end.)
-
- {\em Macintosh User Interface Guidelines}, in Inside Macintosh,
- Apple Computer, Inc., 1984. (Yes, Atarians, we have something to
- learn here. Though not everything "translates", this is a fine
- piece of principled design work. Read and appreciate.)
-
- James D.~Foley, Victor L.~Wallace, and Peggy Chan,
- {\em The Human Factors of Computer Graphics Interaction Techniques},
- IEEE Computer Graphics (CG \& A), November 1984, pp. 13--48.
- (A good overview, including higher level topics which I have
- postponed to a upcoming later section. Excellent bibliography.)
-
- J.~D.~Foley and A.~Van Dam,
- {\em Fundamentals of Interactive Computer Graphics},
- Addison Wesley, 1984, Chapters 5 and 6.
- (If you can't get the article above, read this. If you are designing
- graphics apps, buy the whole book! Staggering bibliography.)
-
- Ben Schneidermann, {\em Direct Manipulation: A Step Beyond
- Programming Languages}, IEEE Computer, August 1983, pp. 57--69.
- (What do Pacman and Visicalc have in common? Schneidermann's
- analysis is vital to creating hot interfaces.)
-
-
- \newpage
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \section{GEM Events and Program Structure}
-
- So I fibbed a little. This chapter of ST Professional GEM started
- out to be another discussion of interface issues. But, as Tolkien
- once said, the tale grew in the telling, and this is now the first
- of a series of three articles. This part will discuss AES event
- handling and its implications for GEM program structure. The
- following will contain a "home brew" dialog handler with
- some new features, and will, finally, take up the
- discussion of interface design, using the dialog handler as an
- example.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{All for one, and one for all}
-
- A quick inspection of the AES
- documents shows that there are five routines devoted to waiting
- for individual types of events, and one routine, \verb"evnt_multi", which
- is used when more than one type is desired. This article will
- discuss ONLY \verb"evnt_multi" for two reasons. First, it is the most
- frequently used of the routines. Second, waiting for one type of
- event is a bad practice. Any event call turns the system over to
- the AES and suspends the application and its interaction with the
- user. In such cases, some "escape clause", such as a timer,
- should be inserted to revive the program and prompt the user if no
- event is forthcoming. Otherwise, the application may end up
- apparently (or actually) hung, with a resulting reboot, and
- probably a very annoyed user.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsubsection{Starting ahead}
-
- One possible type of event is a message.
- Messages are usually sent to the application by the AES, and are
- associated with windows or the menu. We have already
- discussed such messages:
- we considered window messages, and we handled menu
- messages. You may want to review these topics before proceeding.
-
- The actual \verb"evnt_multi" call is a horrendous thing:
-
- \begin{verbatim}
- ev_which = evnt_multi(ev_flags,
- btn_clicks, btn_mask, btn_state,
- r1_flags, r1_x, r1_y, r1_w, r1_h,
- r2_flags, r2_x, r2_y, r2_w, r2_h,
- &msg_buff,
- time_lo, time_hi,
- &mx, &my, &btn, &kbd, &char, &clicks);
- \end{verbatim}
-
- Each of the lines in the call relate to a different event, and
- they will be discussed in the order in which they appear.
-
- Note that a call with this number of parameters causes some
- overhead due to stacking and retrieval of the values. In most
- cases, this should be of little concern on a machine as fast as
- the ST. However, where throughput is a concern, such as in close
- tracking of the mouse cursor, you may want to write a customized
- binding for \verb"evnt_multi" which dispenses with the parameter list.
- This can be accomplished by maintaining the values in a static
- array and moving them as a block into the binding arrays \verb"int_in"
- (for all values but \verb"&msg_buff"), and \verb"addr_in" (for \verb"&msg_buff"). Note
- that you may {\em not}\/ simply leave the values in \verb"int_in"; other AES
- bindings reuse this space.
-
- {\tt ev\_flags} and {\tt ev\_which} are both 16-bit integers composed of
- flag bits. Bits set in {\tt ev\_flags}\/ determine which event(s) the call
- will wait for; those set in {\tt ev\_which}\/ indicate what event(s)
- actually occurred. Both use the following flag bit mnemonics and
- functions:
-
- \vskip.5cm
- \begin{center}
- \begin{tabular}{c|l}
- 0x0001 & {\tt MU\_KEYBD} --- Keyboard input\\
- 0x0002 & {\tt MU\_BUTTON} --- Mouse button(s)\\
- 0x0004 & {\tt MU\_M1} --- Mouse rectangle \#1\\
- 0x0008 & {\tt MU\_M2} --- Mouse rectangle \#2\\
- 0x0010 & {\tt MU\_MESAG} --- AES message\\
- 0x0020 & {\tt MU\_TIMER} --- Timer\\
- \end{tabular}
- \end{center}
- \vskip.5cm
-
- The appropriate mnemonics are ORed together to create the proper
- {\tt ev\_flags}\/ value.
-
- There is one common pitfall here. Notice that multiple
- events may be reported from one \verb"evnt_multi". Event merging is
- performed by the AES in order to save space on the application's
- event queue. If events have been merged, more than one bit will
- be set in the {\tt ev\_which}\/ word. Your application must check ALL of
- the bits before returning to a new {\tt evnt\_multi}\/ call. If you
- don't do this, some events may be effectively lost.
-
- The first event to be considered is the mouse button. This
- is probably the most difficult event to understand and use.
-
- The parameter {\tt btn\_clicks} tells GEM the maximum number of
- clicks which you are interested in seeing. This value is usually
- two, if your program uses the double-click method, or one if only
- single clicks are used. The AES returns the number of clicks
- which caused the event through {\tt \&clicks}, which must be a pointer to
- a word.
-
- GEM determines the number of clicks by the following method.
- When the first button-down is detected, a time delay is begun. If
- another complete button-up, button-down cycle is detected before
- the time expires, then the result is a double click. Otherwise,
- the event is a single click. Note that the final state of the
- buttons is returned via {\tt \&btn}, as described below. By checking
- this final state, you may determine whether a single click event
- ended with the button up (a full click), or with the button still
- down (which may be interpreted as the beginning of a drag
- operation). Double clicking is meaningless, and not checked, if
- the \verb"evnt_multi" is waiting on more than one button (see below).
-
- The double-click detection delay is variable, and may be set
- by your program using the call
-
- \begin{verbatim}
- ev_dspeed = ev_dclick(ev_dnew, ev_dfunc);
- \end{verbatim}
-
- {\tt ev\_dfunc}\/ is a flag which determines the purpose of the call. If
- it is zero, the current double click speed is returned in
- {\tt ev\_dspeed}\/. If {\tt ev\_dfunc}\/ is non-zero, then {\tt ev\_dnew}\/
- becomes the new double-click speed. Both {\tt ev\_dspeed}\/ and
- {\tt ev\_dnew}\/ are words containing a {\tt magic number}\/ between
- zero and four. Zero is the
- slowest (i.e., longest) double-click, and four is the fastest.
- (These correspond to the slow-fast range in the Desktop's
- Preferences dialog.) In general, you should not reset the click
- speed unless specifically requested, because such a change can
- throw off the user's timing and destroy the hand/eye coordination
- involved in using the mouse.
-
- GEM was originally designed to work with a single button
- input device. This allows GEM applications to function well with
- devices such as light pens and digitizing tablets. However, some
- features are available for dealing with multi-button mice like the
- ST's.
-
- The \verb"evnt_multi" parameters {\tt btn\_mask}\/ and {\tt btn\_state}\/ are words
- containing flag bits corresponding to buttons. The lowest order
- bit corresponds to the left-most button, and so on. A bit is set
- in the {\tt btn\_mask}\/ parameter if the AES is to watch a particular
- button. The corresponding bit in {\tt btn\_state}\/ is set to the value
- for which the program is waiting. The word returned via {\tt \&btn}\/
- uses the same bit system to show the state of the buttons at
- completion. It is important to notice that all of the target
- states in {\tt btn\_state}\/ must occur {\em simultaneously}\/ for the event to be
- triggered.
-
- Note the limiting nature of this last statement. It prevents
- a program from waiting for {\em either}\/ the left or right button to be
- pressed. Instead, it must wait for {\em both}\/ to be pressed or to
- be released, which is a
- difficult operation at best. As a result, many people thought
- the standard mouse
- button procedure is practically useless if you want to take full
- advantage of both buttons on the ST mouse.
-
- Fortunately there is a way out. If the upper byte of {\tt btn\_clicks}
- is set to one (by adding 256 to {\tt btn\_clicks}) the mouse event AES waits
- for can be negated. So instead of waiting for "both mouse buttons released"
- AES will wait for "NOT both mouse buttons released" which is actually
- "one of the mouse buttons pressed". By reading {\tt \&btn} we can then
- easily determine which button was pressed. So setting {\tt btn\_clicks})
- to 256+2, {\tt btn\_mask}) to 3 and {\tt btn\_state}) to 0 will do the
- trick!
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsubsection{Mouse Rectangles}
-
- One of GEM's nicer features is its ability
- to watch the mouse pointer's position for you, and report an event
- only when it enters or departs a given screen region. Since you
- don't have to track the mouse pixel by pixel, this eliminates a
- lot of application overhead. The \verb"evnt_multi" call gives you the
- ability to specify one or two rectangular areas which will be
- watched. An event can be generated either when the mouse pointer
- enters the rectangle, or when it leaves the rectangle. The "r1\_"
- series of parameters specifies one of the rectangles, and the
- "r2\_" series specifies the other, as follows:
-
- \vskip.5cm
- \begin{center}
- \begin{tabular}{r|l}
- r1\_flag, r2\_flag & zero if waiting to enter rectangle,\\
- & one if waiting to leave rectangle\\
- r1\_x, r2\_x & upper left X raster coordinate of wait rectangle\\
- r1\_y, r2\_y & upper left Y raster coordinate of wait rectangle\\
- r1\_w, r2\_w & width of wait rectangle in pixels\\
- r1\_h, r2\_h & height of wait rectangle in pixels
- \end{tabular}
- \end{center}
- \vskip.5cm
-
- Each rectangle wait will only be active if its associated flag
- (\verb"MU_M1" or \verb"MU_M2") was set in {\tt ev\_flags}\/.
-
- There are two common uses of rectangle waits. The first is
- used when creating mouse-sensitive regions on the screen. Mouse-
- sensitive regions, also called "hot spots", are objects which show
- a visual effect, such as inversion or outlining, when the mouse
- cursor moves over them. The items in a menu dropdown, or the
- inversion of Desktop icons during a drag operation, are common
- examples.
-
- Hot spots are commonly created by grouping the sensitive
- objects into one or two areas, and then setting up a mouse
- rectangle wait for entering the area. When the event is
- generated, the {\tt \&mx} and {\tt \&my} returns may be examined to find the
- true mouse coordinates, and \verb"objc_find" or some other search will
- determine the affected object. The object is then highlighted,
- and a new wait for exiting the object rectangle is posted.
-
- The second common use of rectangle waits is in animating a
- drag operation. In many cases, you can use standard AES animation
- routines such as \verb"graf_dragbox" or \verb"graf_rubberbox". In other cases,
- you may want a figure other than a simple box, or desire to
- combine waits for other conditions such as keystrokes or
- collision with hotspots. Then you will need to implement the drag
- operation yourself, using the mouse rectangles to track the
- cursor.
-
- If you want to track the cursor closely, simply wait for exit
- on a one pixel rectangle at the current position, and perform the
- animation routine at each event. If the drag operation only works
- on a grid, such as character positions, you can specify a larger
- wait rectangle and only update the display when a legal boundary
- is crossed.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsubsection{Messages}
-
- The {\tt \&msg\_buff}\/ parameter of \verb"evnt_multi" gives the
- address of a 16 byte buffer to receive an AES message. As noted
- above, I have discussed standard AES messages elsewhere. The last
- column also mentioned that messages may be used to simulate co-
- routines within a single GEM program.
-
- A further possibility which bears examination is the use of
- messages to coordinate the activities of multiple programs. In
- single-tasking GEM, at least one of these programs would have to
- be a desk accessory. In any such use of the GEM messages, you
- should pay careful attention to the possibility of overloading the
- queue. Only eight slots are provided per task, and messages,
- unlike events, cannot be merged by the AES.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsubsection{Timer}
-
- The timer event gives you a way of pacing action on
- the screen, clocking out messages, or providing a time-out exit
- for an operation. \verb"Evnt_multi" has two 16-bit timer input
- parameters, {\tt time\_hi}\/ and {\tt time\_lo}\/, which are the top and bottom
- halves, respectively, of a 32-bit millisecond count. However,
- this documented time resolution must be taken with a grain of salt
- on the ST, considering that its internal clock frequency is 200Hz!
-
- The timer event is also extremely useful for polling the
- event queue. A "poll" tests the queue for completed events
- without going into a wait state if none are present. In GEM, this
- is done by generating a null event which always occurs
- immediately. A timer count of zero will do just that.
-
- Therefore, you can poll for any set of events by specifying
- them in the \verb"evnt_multi" parameters. A zero timer wait is then
- added to ensure immediate completion. Upon return, if any event
- bit(s) {\em other}\/ than \verb"MU_TIMER" are set, a significant event was found
- on the queue. If only \verb"MU_TIMER" is set, the poll failed to find an
- event.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsubsection{Keyboard}
-
- There are no input parameters for the keyboard
- event. The character which is read is returned as a 16-bit
- quantity through the {\tt \&char} parameter. For historical reasons, the
- codes which are returned are compatible with the IBM PC's BIOS
- level scan codes. You can find this character table in Appendix D
- of the GEM VDI manual. In general, the high byte need only be
- considered if the lower byte is zero. If the low byte is non-
- zero, it is a valid ASCII character.
-
- \verb"evnt_multi" also returns the status of several modifier keys
- through the {\tt \&kbd} parameter. This word contains four significant
- bits as follows:
-
- \vskip.5cm
- \begin{center}
- \begin{tabular}{c|l}
- 0x0001 & Right hand shift key\\
- 0x0002 & Left hand shift key\\
- 0x0004 & Control key\\
- 0x0008 & ALT key\\
- \end{tabular}
- \end{center}
- \vskip.5cm
-
- If a bit is one, the key was depressed when the event was
- generated. Otherwise, the key was up. Since the state of these
- keys is already taken into account in generating the {\tt \&char}\/ scan
- code, the {\tt \&kbd}\/ word is most useful when creating enhanced mouse
- functions, such as shift-click or control-drag.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsubsection{Random notes on events}
-
- Although the {\tt \&mx}, {\tt \&my}, {\tt \&btn}, and
- {\tt \&kbd}\/ returns are nominally associated with particular event types,
- they are valid on any return from \verb"evnt_multi", and reflect the last
- event which was merged into that return by the AES. If you want
- more current values, you may use \verb"graf_mkstate" to resample them.
- Whichever method you choose, be consistent within the application,
- since the point of sampling has an effect on mouse and keyboard
- timing.
-
- Although this and preceding chapters have been presented in
- terms of a GEM application, the event system has many interesting
- implications for desk accessories. Since the AES scheduler uses
- non-preemptive dispatching, accessories have an event priority
- effectively equal to the main application. Though "typical"
- accessories wait only for \verb"AC_OPEN" or \verb"AC_CLOSE" messages when in
- their quiescent state, this is not a requirement of the system.
- Timer and other events may also be requested by an accessory.
- (Indeed, there is no absolute requirement that an accessory
- advertise its presence with a \verb"menu_register" call.) The aspiring
- GEM hacker might consider how these facts could be used to create
- accessories similar to "BUGS" on the Mac, or to the "Crabs"
- program described in the September, 1985 issue of Scientific
- American.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsubsection{Events and GEM Program Structure}
-
- Although the \verb"evnt_multi"
- call might seem to be a small part of the entire GEM system, its
- usage has deep implications for the structure of any application.
- It is generally true that each use of \verb"evnt_multi" corresponds to a
- mode in the program. For instance, \verb"form_do" contains its own
- \verb"evnt_multi", and its invocation creates a moded dialog. While the
- dialog is in progress, other features such as windows and menus
- are unusable. The \verb"graf_dragbox", \verb"graf_rubberbox", and \verb"graf_slidebox"
- routines also contain \verb"evnt_multi" calls. They create a mode which
- is sometimes called "spring-loaded", since the mode vanishes when
- some continuing condition (a depressed mouse button) is removed.
-
- In consequence, a well-designed, non-modal GEM program will
- contain only one explicit \verb"evnt_multi" call. This call is part of a
- top-level loop which decodes events as they are received and
- dispatches control to the appropriate handling routine. The
- dispatcher must always distinguish between event types. In
- programs where multiple windows are used, it may also need to
- determine which local data structure is associated with the active
- window.
-
- This construction is sometimes called a "push" program
- structure, because it allows the user to drive the application
- by generating events in any order. This contrasts with the "pull"
- structure of traditional command line or menu programs, where the
- application is in control and demands input at each step before it
- proceeds. "Push" structure promotes consistent use of the
- user interface and a feeling of control on the part of the user.
-
-
- \newpage
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \section{A new Form Manager}
-
- This is the first chapter
- devoted to explaining a large piece of code. This article is also
- the second in a series of three concerning GEM user interface
- techniques. The code is an alternate form (dialog) manager for
- GEM. It is listed as {\tt GEMCL13.C}.
-
- What is unique about this version of the form manager?
- First, it implements all of the functions of the standard GEM
- \verb"form_do" routine, as well as adding a "hot spots" feature which
- causes selectable objects to become mouse-sensitive, just like
- the entries in menu dropdowns. The second (and obvious)
- difference is that this form manager is provided in source code
- form. This gives you the freedom to examine it and change it to
- suit your own needs.
-
- I have several purposes in presenting this code. It is
- intended as an example of GEM program structure, and an
- application of some of the techniques presented in earlier
- columns. It is also relevant to the continuing thread discussing
- the necessity of feedback when constructing a user interface.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{On with the show}
-
- Taking your listing in hand, you will
- quickly notice two things. First, this program uses the infamous
- portability macros, so that it may be used with Intel versions of
- GEM. Second, the routines are arranged "bottom up", with the main
- at the end, and subroutines going toward the beginning. (This is
- a carry-over from my days with ALGOL and PASCAL.) You should now
- turn to the \verb"form_do" entry point near the end of the code.
-
- One change has been made in the standard calling sequence for
- \verb"form_do". The starting edit field is now a pointer to a value,
- rather than the value itself. The new \verb"form_do" overwrites the
- initial value with the number of the object being edited when the
- dialog terminated. Using this information, your program can
- restore the situation when the dialog is next called. As before,
- if there is NO editable field, the initial value should be zero.
-
- There are several local variables which maintain vital state
- information during the dialog interaction. \verb"Edit_obj" is the number
- of the editable object currently receiving keystrokes. \verb"Next_obj"
- is set when the mouse is clicked over an object. If the object
- happens to be editable, \verb"next_obj" becomes the new \verb"edit_obj".
-
- Three variables are associated with the "hot-spot" feature.
- If \verb"hot_mode" is set to \verb"M1_ENTER", then the mouse is outside the area
- of the dialog. If it equals \verb"M1_EXIT", then the mouse is currently
- in the dialog. If it is in the dialog, \verb"hot_obj" indicates whether
- there is an active "hot" object. If its value is NIL (-1), then
- there is no active object. Otherwise, it is equal to the number
- of the object which is currently "hot", that is, inverted on the
- screen. Finally, \verb"hot_rect" is the current wait rectangle. If the
- mouse is outside of the window, then the wait rectangle equals the
- dialog's ROOT. If there is a current hot object, then \verb"hot_rect"
- equals that object's screen rectangle. If the mouse is in the
- dialog, but not within a hot object, then the wait rectangle
- defines the area within which no further collision checks are
- necessary. This is arrived at through an algorithm explained
- below.
-
- \verb"Form_do's" initialization code sets up the hot-spot variables
- to trigger if the mouse is within the dialog. It also sets
- starting values for \verb"edit_obj" and \verb"next_obj" which will cause the
- edit startup code to be activated.
-
- The main portion of \verb"form_do" is a loop, exhibiting the type of
- event driven structure discussed in the last column. Before
- entering the \verb"evnt_multi" wait, the status of \verb"next_obj" and \verb"edit_obj"
- are checked to see if a new object should be initialized for
- editing. If so, \verb"objc_edit" is called with the \verb"EDINIT" function
- code. NOTE: the \verb"objc_edit" calling sequence used in this program
- differs from the one given in the AES manual, which is incorrect!
- You should check the bindings you are using to be sure they will
- work with this code, and modify as necessary.
-
- The \verb"evnt_multi" is set up to wait for a mouse click (single or
- double), for a keyboard input, or for the mouse to make a
- "significant" movement, as discussed above. Notice that since
- \verb"form_do" is used as a subroutine, it does not handle messages which
- are normally processed by the main loop of your application.
- Notice that this creates a mode, and that this routine as written
- handles modal dialogs. You could, however, use this code as the
- basis for a non-modal dialog handler by drawing the dialog within
- a window, and combining the main loop of \verb"form_do" with the main
- loop of your application. (This possibility may be examined in
- the future. In the meantime, it is left as an exercise for
- the reader.)
-
- The event bit vector is returned to the variable "which".
- Since events are not mutually exclusive, each possible event type
- must be examined in turn before returning to the \verb"evnt_multi" call.
- The form manager's event handling routines are \verb"form_hot", for mouse
- rectangle event, \verb"form_keybd", for character input, and \verb"form_button",
- for mouse clicks. \verb"Form_keybd" and \verb"form_button" are allowed to
- terminate the dialog by returning a value of false to the loop
- control variable "cont". If termination is imminent, or the user
- has clicked on a new editable object, \verb"objc_edit" is called with
- \verb"EDEND" to remove the cursor from the old object. The normal flow
- of control then returns to edit setup and \verb"evnt_multi".
-
- A few cleanup actions are performed upon termination. If the
- terminating object (stored in \verb"next_obj") is not the same as the
- \verb"hot_obj", then a race condition has occured and the hot object must
- be cleared with \verb"objc_toggle" before exiting. After this test, the
- final \verb"edit_obj" value is passed back via the parameter, and the
- terminating object is returned as the function value.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Relaxen und Watchen das Blinkenlichten}
-
- \verb"form_hot" is
- responsible for maintaining on-screen hot-spots, and correctly
- updating the internal hot-spot variables. It is about halfway
- through the listing.
-
- The first action in \verb"form_hot" is to determine if the mouse has
- just exited an object which is hot. In this case, \verb"objc_toggle" is
- called to unhighlight the object and reset the \verb"SELECTED" flag.
-
- The current mouse position is passed to \verb"form_hot" by \verb"form_do".
- It is checked against the root rectangle of the dialog to see if
- the mouse is inside the dialog. If not, the program must wait for
- it to re-enter, so \verb"form_hot" sets the rectangle and waiting mode
- accordingly, and returns NIL as the new \verb"hot_obj".
-
- When the mouse is within the dialog, a regular \verb"objc_find" call
- determines the object at which it is pointing. For an object to
- be mouse-sensitive, it must be \verb"SELECTABLE" and not \verb"DISABLED". If
- the found object meets these tests, the mouse will "hover" over
- the object, waiting to leave its screen rectangle. Since the
- object might already be \verb"SELECTED" (and hence drawn reversed), this
- is checked before \verb"objc_toggle" is called to do the highlighting and
- selection of the object, which becomes the \verb"hot_obj". (If the
- object was already \verb"SELECTED", the \verb"hot_obj" becomes NIL.)
-
- The toughest condition is when the mouse is within the
- dialog, but not over a mouse-sensitive object. The regular GEM
- event structure will not work, because it can only wait on two
- rectangles, and there may be many more selectable objects in a
- dialog tree. I have found a way around this limitation using a
- combination of the \verb"map_tree" utility
- with the principle of visual hierarchy in object trees.
-
- In summary, the algorithm attempts to find the largest
- bounding rectangle around the current mouse position, within which
- there are no mouse-sensitive objects. It starts with a rectangle
- equal to the dialog root, and successively "breaks" it with the
- rectangle of each mouse-sensitive object. The next few paragraphs
- examine this method in detail.
-
- Since C lacks the dynamic scoping of LISP, from which
- \verb"map_tree" was derived, it is necessary to set up some globals to be
- used during the rectangle break process. \verb"Br_rect" is the \verb"GRECT" of
- the current bounding rectangle. \verb"Br_mx" and \verb"br_my" hold the current
- mouse position. \verb"Br_togl" is a switch which determines whether the
- next break will be attempted horizontally or vertically. After
- initializing these variables, \verb"form_hot" uses \verb"map_tree" to invoke the
- \verb"break_obj" routine for every object in the dialog.
-
- \verb"Break_obj" first intersects the rectangle of the object with
- the current bounding rectangle. If they are disjoint, then
- neither the object nor any of its offspring can possible affect
- the operation, so FALSE is returned, causing \verb"map_tree" to ignore
- the subtree.
-
- The object is next checked to see if it is mouse-sensitive.
- As before, it must be \verb"SELECTABLE" and not \verb"DISABLED", and it must not
- be hidden (this was checked automatically by \verb"objc_find" before).
- If these conditions are met, then the object intrudes into the
- current bounding rectangle. To maintain the desired condition,
- part of the rectangle must be removed or "broken away".
-
- In many cases, the break operation can be done either
- horizontally or vertically. Since we have no prior information as
- to which way the mouse will move next, \verb"break_obj" uses the \verb"br_togl"
- flag to alternate which direction it will try first. This should
- yield the most nearly square rectangle.
-
- The \verb"break_x" and \verb"break_y" routines are very similar. In each
- case, the segment occupied by the breaking object is compared to
- the point occupied by the mouse. If the point is within the
- segment, there is no possible break in this dimension, and FALSE
- is returned. If the point lies outside the segment, then the
- rectangle may be successfully broken by reducing this dimension.
- This is done, and TRUE is returned to report success.
-
- The \verb"break_y" routine also employs a look-ahead test to prevent
- a possible infinite loop. It is conceivable, though not likely,
- that someone might nest a non-\verb"SELECTABLE" object completely within
- another \verb"SELECTABLE" object(s). If the mouse point is within such
- an object, the algorithm will not be able to select a break
- dimension. In the current version, the mouse rectangle is simply
- forced to a single pixel for this case. (Note that is is NOT
- sufficent to simply wait on the non-selectable object's rectangle,
- since other \verb"SELECTABLE" objects may overlap it and follow it in
- tree order.)
-
- Since \verb"map_tree" examines all possible objects, \verb"br_rect" will be
- the correct bounding rectangle at completion. Note that you can
- readily adapt this technique to other uses, such as hot-spotting
- while dragging objects. It is much less demanding of CPU
- resources than other methods, such as repetitive \verb"objc_finds".
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{What a character!}
-
- The \verb"form_keybd" routine acts as a filter on
- character input. When it recognizes a control character, it
- processes it and zeroes the keyboard word. Other chararacters are
- passed on to \verb"objc_edit" to be inserted in \verb"edit_obj". If there is no
- editing object, the character goes to the bit bucket.
-
- The \verb"form_keybd" given implements the standard GEM
- functionality with two minor additions. First, a carriage return
- in a dialog with no \verb"DEFAULT" exit object is taken as a tab. This
- allows <CR> to be used "naturally" in dialogs with several lines
- of text input. Second, tabs and backtabs "wrap around" from top
- to bottom of the dialog, and are done by "walking the tree",
- rather than relying on the \verb"LASTOB" flag to signal the end of the
- dialog. This allows the new form manager to handle dialog trees
- which are not contiguous in memory.
-
- The code sets up several global variables for use by mapped
- functions. \verb"fn_obj" is the output from both \verb"find_tab" and
- \verb"find_def". \verb"fn_dir" is an input to \verb"find_tab".
- \verb"Fn_last", \verb"fn_prev", and \verb"fn_last" are used while
- searching for tab characters.
-
- A carriage return results in a search of the entire tree,
- using \verb"map_tree" and \verb"find_def", for an object with its
- \verb"DEFAULT" flag set. Its \verb"SELECTED" flag is set and it is
- inverted on the screen to indicate the action taken.
- \verb"Form_keybd" returns a FALSE to force termination of the main
- \verb"form_do" loop. If no \verb"DEFAULT" is found, control passes
- to the tab code.
-
- The tabbing procedure is somewhat complicated because the
- same code is used for forward and backward tabbing. The old value
- of \verb"edit_obj" (the object being tabbed FROM) is placed into
- \verb"fn_last". \verb"fn_dir" is set to one for a forward tab, and
- zero for a backward tab.
-
- The general strategy is to scan the entire tree for \verb"EDITABLE"
- objects, always saving the last one found in \verb"fn_prev". When
- tabbing forward \verb"fn_last" is checked against \verb"fn_prev".
- A match indicates that the current object is the one desired. When
- tabbing backward the current object is checked against \verb"fn_last".
- If they match, \verb"fn_prev" is the desired object. This procedure
- requires two passes when the tab "wraps around" the tree, that is,
- when the desired object as at the opposite end of the traverse
- from the old editing object.
-
- The result of the tab operation is written back into
- \verb"form_do's" \verb"next_obj" parameter, and becomes the new editing
- object at the beginning of the next loop.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Button down}
-
- The \verb"form_button" procedure is lengthy because it
- must recognize and handle mouse clicks on several types of
- objects: \verb"EDITABLE", \verb"SELECTABLE", and \verb"TOUCHEXIT". The first section
- of code rejects other objects, which cannot accept a click.
-
- The next piece of \verb"form_button" makes a special check for a
- double click on a \verb"TOUCHEXIT" object. This will cause the high bit
- of the returned object number to be set. (By the way, this also
- occurs in the standard \verb"form_do".) This flag allows user dialog
- code to perform special processing on the object.
-
- The largest piece of \verb"form_button" handles the various cases in
- which the \verb"SELECTABLE" flag may be set. Setting the \verb"RBUTTON" (radio
- button) flag causes all of the object's siblings in the tree to be
- deselected at the time the object is clicked. The \verb"do_radio"
- routine uses the \verb"get_parent" utility to find the ancestor, and then
- performs the deselect/select operation.
-
- If the \verb"SELECTABLE" object is not \verb"TOUCHEXIT", then \verb"graf_watchbox"
- is used to make sure that the mouse button comes back up while it
- is within the object. Otherwise, the click is cancelled. Care is
- necessary here, since the hot-spot code may have already set the
- \verb"SELECTED" flag for the object. (We cannot be sure of this, for a
- race condition may have occurred!)
-
- If the \verb"SELECTABLE" object is \verb"TOUCHEXIT", then the application
- has requested that \verb"form_do" exit without waiting for the button to
- go back up. In both this and regular \verb"form_do", \verb"TOUCHEXIT" objects
- are used when you want to provide immediate response (animation)
- within the context of a dialog.
-
- The final parts of \verb"form_button" do cleanup. If the clicked
- object was already hot-spotted, \verb"hot_obj" must be reset to NIL,
- otherwise \verb"form_do" will carefully unselect the object which has
- just been selected!
-
- If the \verb"EXIT" or \verb"TOUCHEXIT" flags are in force, \verb"form_button"
- returns FALSE to force the completion of \verb"form_do". For \verb"EDITABLE"
- objects, \verb"next_obj" is left intact to replace \verb"edit_obj" during the
- next loop. Otherwise, \verb"next_obj" has done its job and is zeroed,
- and \verb"form_button" returns TRUE for continuation.
-
- This concludes the tour of the alternate \verb"form_do". The best
- cure for any confusion in this explanation is to compile the code
- into an application and watch how it runs with different
- resources, or attack it with a debugger.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \subsection{Operators are standing by}
-
- I encourage you to modify this
- code to meet your particular needs and incorporate it into your
- application. I would like to request than anyone who comes up
- with significant improvements (or bug fixes) send them to me so
- they can be made generally available. You can do this via the
- ANTIC ONLINE Feedback, or by sending E-mail to 76703,202.
- \newpage
-
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \newpage
- \section{Coping With GEMDOS}
-
- While it's fun playing with windows and object trees, one of the day-to-
- day realities of working with the ST is its operating system, GEMDOS. A
- successful application should insulate the user from the foibles and
- occasional calamities of the machine's file system. The GEM environment
- provides some minimal tools for doing this, but a good deal of
- responsibility still rests with you, the programmer.
-
- This chapter tries to address the GEM/DOS
- integration problem by providing you some stock code for common
- functions, along with a discussion of some of the worst "gotchas"
- lurking for the unwary. The source for this column is called {\tt GEMCL15.C}.
-
-
- \subsection{Another bit of history}
-
- There has been a good deal of confusion in the Atari press and among
- developers over what GEMDOS is, and how it relates to TOS and CP/M-68K.
- It's important to clear this up, so you can get a true picture of what
- GEMDOS is intended to do. The best way is to tell the story of GEMDOS'
- origins, which I can do, because I was there.
-
- As most developers are aware, GEM was first implemented on the IBM PC.
- PC GEM performed two functions. The first was a windowed graphics
- extension to the PC environment. The second was a visual shell, the
- Desktop, which ran on top of the existing operating system, PC-DOS.
-
- When work started on moving GEM to the ST, there were two big problems.
- First, no STs actually existed. Second, there was no operating system on
- the 68000 with which GEM and the Desktop could run. Unix was too large,
- and CP/M-68K lacked a number of capabilities, such as hierarchical
- files, which were needed to support GEM.
-
- Work on porting the graphics parts of GEM to the 68000 had to start
- immediately to meet schedules. Therefore, CP/M-68K running on Apple
- Lisa's was used to get this part of the project off the ground.
- Naturally, the Alcyon C compiler and other tools which were native to
- this environment were used.
-
- In parallel, an effort was begun to write a new operating system for the
- 68000, which would ultimately become the ST's file system. It was
- designed to be a close clone of PC-DOS, since it would perform the same
- functions for GEM in the new environment. At this point, the term TOS
- was introduced. TOS really meant "the operating system, whatever it may
- be, that will run on the ST", since not even the specifications, let
- alone the code, were complete at that time.
-
- The first engineer to work on "TOS" at Digital Research was Jason
- Loveman. This name leaked to the press, and in some distorted fashion
- generated a rumor about "Jason DOS", which was still just the same
- unfinished project. As "TOS" became more solid, the developer's tools
- were ported to the new environment one by one, and the GEM programming
- moved with them. CP/M-68K was completely abandoned, though the old
- manuals for C and the tools lived on and are still found in the Atari
- developer's kit.
-
- All of this work had been done on Lisas or Compupro systems fitted with
- 68000 boards. At this point, workable ST prototypes became available. An
- implementation of "TOS" for the target machine was begun, even before
- the basic operating system was fully completed.
-
- The other intent for the new operating system was to be a base for GEM
- on other 68000 systems as well as the ST. Because of this, Digital
- Research named it GEMDOS when it was finally complete, thus providing
- the final bit of nomenclature. "TOS" as now found in the ST is in fact a
- particular implementation of generic GEMDOS, including the ST specific
- BIOS.
-
- So, GEMDOS is a PC-DOS clone, but, not quite. There are enough
- differences to cause problems if they are ignored. (Remember, it looks
- like a duck, and quacks like a duck, but it's not a duck.)
-
-
- \subsection{Going for it}
-
- As a first example, consider the routines {\tt open\_file()} and {\tt create\_file()}
- at the beginning of the listing. They make use of the GEMDOS calls
- \verb"Fopen()" and \verb"Fcreate()". You will notice that these names are not the ones
- specified in the Digital Research GEMDOS manual. Developers who have
- used PC GEM will also observe that they are radically different from the
- function names in the PC-DOS bindings.
-
- In fact, all of the GEMDOS function calls on the ST are defined as
- macros in the file osbind.h, distributed with the developer's kit. At
- compile time they are turned into calls to the assembly language routine
- {\tt gemdos()}, part of the osbind.o binary. So, if you find the naming
- conventions to be particularly offensive for some reason, just edit the
- appropriate macros in osbind.h.
-
- In DRI's PC-DOS bindings, any error codes were returned in the global
- variable \verb"DOS_ERR". In the GEMDOS bindings, the operation result or an
- error code is returned as the value of the calling function. In the case
- of \verb"Fopen()" and \verb"Fcreate()", the result is a valid file handle if it is
- positive. A negative result is always an error code, indicating that the
- operation failed.
-
- An application which encounters a GEMDOS error should display an alert,
- and query for retry or abort. The type of loop structure exemplified by
- {\tt open\_file()} and {\tt create\_file()} should be usable with most GEMDOS
- functions which might fail. The AES provides a function, \verb"form_error",
- which implements a set of "canned" error alerts appropriate to the
- various possible errors.
-
- However, this is where the fun starts. For unknown reasons, the
- \verb"form_error" on the ST expects to see PC-DOS, not GEMDOS, error codes as
- it's input! Therefore you need a routine to translate one into the
- other. The routine {\tt dos\_error()} in the listigs provides this function.
- The GEMDOS errors are in the same sequence as those for PC-DOS, but
- their numerical order is reversed and shifted. Notice also that
- {\tt dos\_error()} does NOT perform the translation if the error code is less
- than -50. These codes have no PC-DOS equivalent; computing a bogus
- translation will cause \verb"form_error" to crash. Instead, they are passed
- through verbatim, resulting in a "generic" alert which gives only the
- error number.
-
- The other major task in integrating a GEM application with the file
- system is selecting file names for input and output. Again, the AES
- provides some assistance with the \verb"fsel_input" call, which invokes the
- standard file selector dialog.
-
- There are several drawbacks to the standard file selector. One is that
- the "ITEM SELECTOR" title is constant and cannot be changed by the
- application. This could cause confusion for the user, since it may not
- be clear which of several functions, closely spaced in the FILE menu,
- was actually invoked. While it might be possible to find and "rewire"
- the AES resource that defines the file selector, it is unlikely that
- such an approach would be portable to a later version of ST GEM.
-
- A viable approach to eliminating confusion is to display a small
- "marquee" box, with a message defining the operation, on the screen just
- above the file selector. To do this, you must initialize the location of
- the box so that it is outside of the file selector's bounds, and then
- draw it just before invoking the file selector. This way they will
- appear together. Before returning to its main event loop, the
- application should post a redraw message for the "marquee" area. The AES
- will merge this redraw with the one generated by \verb"fsel_input", and the
- result will be received by the application's \verb"evnt_multi".
-
- Later versions (since TOS 1.4) of the ST GEM provide the \verb"fsel_exinput"
- call that allows the programmer to specify a title line. But since this
- call is not available on older machines we have to check which version
- of TOS is installed, and may have to call the old routine after all.
-
- Another problem with the file selector is that it resets your
- application's virtual workstation clip rectangle without warning. There
- are other AES functions, such as \verb"objc_draw", which also do this, but the
- file selector can be troublesome because it may be the only AES call
- used by some VDI-based ST applications.
-
- The veteran developer will also notice that the file selector takes and
- returns the path and filename as two separate strings, while the GEMDOS
- file functions require a fully pathed file name. Also, the file selector
- doesn't remember its "home" directory; you are responsible for
- determining the default directory, and keeping track of any changes. The
- remainder of the listing and chapter is devoted to set of utilities
- which should alleviate some of the "grunt work" of these chores.
-
- The top level routine in this collection is {\tt get\_file()}. It is called
- with two string arguments. The first must point to a four byte string
- area containing the desired file name extension (three characters plus a
- null). The second is the default file name.
-
- If the default file name is non-null, then {\tt get\_file()} invokes
- {\tt parse\_fname()} to break it into path and name. {\tt Parse\_fname()} also adds
- the necessary "wild card" file specification to the path, using the
- extent name given as input.
-
- If no default file was supplied, or the default did not contain a path,
- the routine {\tt get\_path()} is invoked to find the current default directory
- and construct a legal path string for it.
-
- The results of these manipulations are supplied to \verb"fsel_input". Notice
- that the result of the file selector is returned via its third argument,
- rather than as a function value. If the result is TRUE, {\tt get\_file()}
- merges the temporary path and file string, storing the result via the
- second input parameter. This result string is suitable for use with
- Fopen, and may be resubmitted to {\tt get\_file()} when the next operation is
- invoked by the user.
-
- {\tt Parse\_fname()} is straight-forward C. It looks backward along the file to
- find the first character which is part of the path. The tail of the
- filename is copied off, and its former location is overlaid with the
- wild card specification.
-
- {\tt Get\_path()} is a bit more interesting. It makes use of two GEMDOS
- functions, \verb"Dgetdrv()" and \verb"Dgetpath()" to obtain the default disk drive and
- directory, respectively. Note that \verb"Dgetpath()" will return a null string
- if the current default is the root, but it puts a back-slash at the
- beginning of the path otherwise. This forces a check for insertion in
- the root case, since the file selector wants to see something like
- \verb"A:\*.RSC", rather than \verb"A:*.RSC". After making this fix, {\tt get\_path()}
- concatenates the wild card specification derived from the input extent.
-
- The last routine in the listing is {\tt new\_ext()}. This utility is useful if
- your application uses more than one associated file at a time. For
- instance, the Resource Construction Set uses both an RSC and a DEF file,
- with the same base name. {\tt New\_ext()} takes a fully formed file name, and
- replaces its old extent with the new one which you supply. This lets you
- quickly generate both file names after one call to the file selector.
- Notice that {\tt new\_ext()} looks BACKWARD along the name to find the
- delimiting period, since this character can also be part of a
- subdirectory name in the path.
-
- So we reach the end of the code and this chapter. Hopefully both will
- keep you profitably occupied for a while. The next chapter will return to
- graphics topics, with a look at writing customized rubber box and drag
- box routines.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \newpage
- \section{Interface Potpourri}
-
- This chapter of ST PRO GEM presents code
- implementing several user interface techniques: progress indicators,
- rubber boxes, and draggable boxes with mouse sensitive targets. The code
- also includes some utility routines for handling resources, event calls,
- and VDI line drawing.
-
- There are several files for this column. Note the plural - in addition
- to the usual C sources stored in {\tt GEMCL16.C}, the files {\tt GEMCL16.RSC},
- {\tt GEMCL16.DFN}, {\tt GEMCL16.H}, and {\tt GEMCL16.RSH} are a template resource for
- building progress boxes. {\tt GEMCL16.RSC} is the resource binary, and
- {\tt GEMCL16.H} is its symbol binding file, to be used with {\tt GEMCL16.C}. The RSH
- file is a C image of the resource - you would need {\tt STCREATE} to
- regenerate it.
-
- {\tt GEMCL16.DFN} is the binary symbol file for the resource. It is in the
- format used by the NEW ST Resource Construction Set. This fixes a number
- of bugs, and has a much faster user interface.
-
-
- \subsection{Making Progess}
-
- The need for feedback in interface designs has been discussed in
- previous chapters. One instance which is often necessary is the so-called
- progress indicator. A progress indicator is used when your application
- is doing a long operation. It shows that the function is continuing
- satisfactorily, and is not hung in a loop. When possible, it also gives
- an indication of the fraction of the operation which has been completed.
- The thermometer bars on the Desktop format and copy operations are
- examples.
-
- The sample code shows two types of progress indicator. Both are built
- within the structure of a dialog resource. The first type uses a
- variable line of text to describe each phase of an operation as it
- occurs. The rewriting of the text provides action on the screen; the
- fact that it is different each time gives reassurance that the program
- is not hung. The second type of indicator is the thermometer bar. This
- is more useful when the operation is uniform, allowing you to estimate
- the fraction completed. Let's look at the code.
-
- The routines {\tt beg\_prog()} and {\tt end\_prog()} are common to the two types. The
- code is very similar to the standard dialog handling procedure, but is
- broken into two parts. {\tt Beg\_prog()} assumes that the progress indicator
- box is defined by a dialog tree named {\tt PROGRESS}. Such a tree is provided
- in {\tt GEMCL16.RSC}. Beg\_prog() makes the usual calls to center and draw the
- box. The rectangle computed in the centering operation is stored via a
- \verb"GRECT" pointer passed in the parameter. This rectangle compensates for
- the outline around the box, and must be supplied to {\tt end\_prog()} when the
- operation is complete.
-
- The first version of {\tt set\_prog()} in the code implements the changing
- text progress indicator. It looks in a tree labelled \verb"STRINGS" for the
- object number which is passed as a parameter. It is assumed that this
- object is a \verb"G_STRING". The address of the new text is loaded from the
- object's \verb"ob_spec" field. (For those with the new RCS, it would be easy to
- alter this routine to use free strings. Simply replace the first two
- lines with: \verb"rsrc_gaddr(R_STRING, strno, &saddr);" and supply parameters
- which are the names of strings in a \verb"FRSTR" box.)
-
- Once the new text is found, the {\tt set\_text()} utility is called to update
- the \verb"TEDINFO" attached to object \verb"PLINE" in the \verb"PROGRESS" tree. {\tt Set\_text()}
- will insert the new text address in \verb"te_ptext", and the new text length in
- \verb"te_txtlen". {\tt Disp\_obj()} is then used to redraw only the rectangle
- belonging to \verb"PLINE".
-
- \verb"PLINE" must be defined as a \verb"G_BOXTEXT" object with a solid white
- background, and with the \verb"CENTERED" attribute set. It must extend entirely
- across the progress box. This guarantees that the previous text will be
- covered over, and the new text will be centered in the box.
-
- The second version of {\tt set\_prog()} implements the thermometer bar progress
- indicator. The \verb"PROGRESS" tree also includes an object \verb"PROBOX" which
- defines the outline of the thermometer. It is a \verb"G_BOX" object with a
- solid white background, and a one-outside border. The object \verb"PROBAR" is
- nested inside it, with the left edges matching. \verb"PROBAR" is also a \verb"G_BOX",
- with a solid red background and a one-outside border as well. {\tt Set\_prog()}
- creates the thermometer effect by growing and redrawing \verb"PROBAR".
-
- {\tt Set\_prog()} requires two parameters. Maxc is an estimate of the total
- duration of the operation, in arbitrary units. Value is the (new) amount
- completed, in the same units. {\tt Set\_prog} performs two operations. First,
- it computes the fraction value/maxc, and sets \verb"PROBAR" to that fraction of
- the width of \verb"PROBOX". Second, it computes the rectangle which is the
- difference between the old and new widths of \verb"PROBAR", and redraws only
- that part of the progress box. This prevents an annoying flash on the
- screen when the indicator is updated.
-
- These two types of progress indicators have been presented in separate
- routines for convenience in explanation. You can easily combine them in
- a single procedure to create an indicator with both effects.
-
- The final progress indicator routine is called {\tt esc\_prog()}. During many
- lengthy operations is desirable to provide an abort option to the user.
- {\tt Esc\_prog()} lets you do this by polling the keyboard for an escape (ESC)
- character. A zero timer value is used to guarantee an immediate return
- if no character is found. Characters other than escape are ignored.
-
- {\tt Esc\_prog()} returns \verb"TRUE" if an abort is requested, and \verb"FALSE" if the
- operation is to continue. In your application, you can either pair calls
- to {\tt set\_prog()} and {\tt esc\_prog()}, or recode {\tt set\_prog()} to automatically make
- the abort check. In any case, you should add an information line to the
- progress box, telling the user how the operation may be halted.
-
- Of course, this type of progress indicator is not the only option
- available on the ST. Other ideas such as changing window titles, or
- displaying a succession of differing icons are equally valid. Sometimes
- the nature of your application may suggest an alternate metaphor. For
- instance, the progress of recalculating a spreadsheet might be indicated
- by darkening successive columns in a miniature image of the sheet.
- Occasionally, the computing operation is visual itself, and will not
- require an explicit indicator. An example is redisplaying objects in a
- 2D or 3D drawing program.
-
-
- \subsection{Boxed in}
-
- The second part of the listing implements two types of user interaction
- using the mouse. The first creates a "rubber box" on the screen, that
- is, a box whose size is controlled by moving the mouse. This is similar
- to the AES \verb"graf_rubberbox" call, but allows the box to move in any
- direction from its origin, while the GEM function only allows movement
- to the lower right. The AES \verb"graf_rubberbox" call does allow movement
- in any direction however if the maximum values for the box are NEGATIVE.
- The resulting rectangle may have to be corrected if the upper left corner
- has changed.
-
- The second technique allows the user to drag the outline of a box around
- the screen using the mouse. Again, this is similar to the AES
- \verb"graf_dragbox" call, but this version is augmented with code which
- "hotspots" selectable objects when the mouse and object pass over them.
- These routines are another illustration of the usage of the \verb"evnt_multi"
- function, and its combination with VDI drawing to create new interaction
- techniques.
-
- The "rubber box" subroutine is called {\tt fourway\_box()}. Its parameters are
- the current VDI handle (NOT a window handle!), and two \verb"GRECT" pointers.
- The first \verb"GRECT" must have its \verb"g_x" and \verb"g_y" initialized with the fixed
- point of the rubber box. The second GRECT contains an outer bound box
- for the stretching action.
-
- {\tt Fourway\_box()} begins by setting the VDI drawing mode and color. The
- exclusive or, black combination guarantees that redrawing a figure twice
- in the same location will exactly erase it. Next, the routine asserts
- the mouse control flag. This stops the window manager from tracking the
- mouse, with the effect that menus will not drop down during the
- operation.
-
- The fixed coordinates are saved in the variables {\tt ox} and {\tt oy}, and an
- initial mouse reading is obtained with \verb"graf_mkstate". At this point, the
- event loop is entered.
-
- At each iteration, the loop finds the upper left most of the fixed
- vertex and the current mouse position, and updates the tracking \verb"GRECT"
- accordingly. A call to the utility {\tt rc\_intersect()} is used to restrict
- the size of the rubber box to the given limiting rectangle. Note that if
- you need a lower limit to the size of the rubber box, it can be achieved
- by adding another \verb"GRECT" pointer "lower" to the parameter list, and using
- the call {\tt rc\_union(lower, rubber);} This works because the union operation
- selects the larger of two rectangles if they are nested.
-
- {\tt Rub\_wait()} will be described in detail below. Its returns are the new
- mouse position, and an indication of the current mouse button state. If
- the button remains down, the loop continues. When the button is
- released, the rubber box terminates, since it is a "spring-loaded" modal
- operation. Before ending, {\tt fourway\_box()} returns mouse control to the
- window manager. The return from the routine is found in the rubber
- \verb"GRECT", and is the final extent of the box.
-
- {\tt Rub\_wait()} is a utility used by both box techniques. Its purpose is to
- do one step of the box animation, and wait for a mouse movement, or the
- release of the button. {\tt Rub\_wait()} preserves the state of the screen.
-
- The first action is to draw an exclusive or'ed dotted line box at the
- given rectangle. Next, {\tt rub\_wait()} calls \verb"evnt_multi" to wait for the mouse
- button to come up, or the mouse to move out of a one pixel rectangle.
- When the event is detected, the same code is used to remove the box. A
- value of \verb"TRUE" is returned if the mouse button is still down; the curious
- logical construction is necessary since BOTH events could occur at once.
-
- A short examination of the {\tt vdi\_xbox()} code is also useful. After
- converting the rectangle to polyline format, the {\tt vdi\_xline()} routine is
- called. {\tt Vdi\_xline} draws a dotted line, but does not use the VDI line
- style attribute. This is avoided because the VDI has problems with
- corner points when drawing styled lines in \verb"XOR" mode. Instead, a
- selection is made from a set of user defined line styles, based on the
- direction of the stroke, and the odd/evenness of the starting horizontal
- pixel. This assures that the figure will be exactly erasable.
-
-
- \subsection{Hot Stuff?}
-
- The drag box routine is more subtle, because care is needed to correctly
- synchronize the movement of the mouse cursor and the box, and the
- highlighting of target objects. The parameters {\tt vdi\_handle} and {\tt limit} are
- identical to those in {\tt fourway\_box()}. The \verb"GRECT" pointed to by box
- contains the width and height of the movable box when {\tt hot\_dragbox()} is
- entered. On exit it also contains the last x,y coordinates of the box.
- The variable tree is a pointer to the root of a resource tree defining
- the hot spots for the drag operation. Only objects tagged \verb"SELECTABLE" are
- hotspotted. {\tt Hot\_dragbox()} returns the number of such an object if the
- box is "dropped" on it, otherwise a NIL is returned.
-
- Initialization proceeds as above, until the \verb"graf_mkstate" call. Here is
- the first potential synchronization problem. If the user moves the mouse
- very quickly after initiating the drag, it may already be outside the
- box by the time \verb"graf_mkstate" samples the position. The \verb"min/max"
- operations given lock the box onto the cursor, no matter where it has
- strayed. The mouse/box offsets, {\tt ox} and {\tt oy}, will remain constant for the
- rest of the operation.
-
- {\tt Hover\_obj} will contain the number of the object which is currently
- highlighted. It is initialized to \verb"NIL", indicating no object is currently
- marked. {\tt Hot\_dragbox()} now enters a loop with termination conditions
- identical to the rubber box.
-
- The current desired position of the box is computed by subtracting the
- box/mouse offset from the current mouse position. The {\tt rc\_constrain()}
- call ensures that the box will not leave the bounding rectangle. Note
- that {\tt rc\_intersect} would not work here - it would alter the size of the
- draggable box, rather than "nudging" it back into the bounds.
-
- Upon return from {\tt rub\_wait()}, a number of conditions must be checked to
- determine the correct object to highlight, if any. First, we must make
- sure that the mouse is actually within the legal bounds. If not, there
- may be an ambiguous selection, with the mouse over one object and the
- box over another. We choose to do nothing in this case, and set
- {\tt hover\_obj} to \verb"NIL". If the mouse is in bounds, \verb"objc_find" looks for a
- target object. If one exists, it must be \verb"SELECTABLE", or it is forced to
- \verb"NIL".
-
- Next the new object, stored in {\tt ret\_obj}, is compared to the old
- highlighted object, in {\tt hover\_obj}. If they are different, a switch must
- be made. Since either could be \verb"NIL", a check is necessary before calling
- {\tt obj\_toggle} to invert/reinvert the screen image of the object. When the
- loop is complete, the final {\tt hover\_obj} is returned to normal state before
- its number is returned.
-
- You may notice that this method of highlighting objects is different
- from the incremental tree descent and rectangles method presented earlier.
- While not as efficient, the \verb"objc_find" technique is simpler to
- code and may be adequate for many uses. If your program will make heavy
- use of the drag box routine, or will have large trees of target objects,
- you may wish to integrate the incremental hotspotting algorithm with
- {\tt hot\_dragbox()}. This would be simple to do; just use \verb"evnt_multi"'s second
- mouse rectangle for the states associated with the hot-spotter. The
- single pixel rectangles would have to remain, in order to maintain the
- animation effects.
-
- \subsection{R2D2, where are you?}
-
- Finally, you may also notice that the so-called portability macros have
- disappeared from the listing. Indeed, they are gone for good. Since the
- beginning of this column, the growth of the ST GEM developer community
- has outstripped that on the PC. It no longer seems appropriate to
- inconvenience ST developers and violate standard C syntax for the sake
- of Intel's design flaws. Those who still need compatibility with the PC
- may achieve it by compiling under Intel large model, or by writing "sed"
- scripts to translate \verb"(tree+obj)->ob_spec" and the like to their macro
- equivalents.
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \newpage
- \section{PC/ST Resource Converter}
-
-
- This chapter of ST PRO GEM is the first to feature a
- complete GEM application. The program converts resource files between
- the ST (68000) and IBM PC (Intel) formats used by the respective
- versions of GEM and the Resource Construction Set.
-
- The Resource Converter will, for the most part, be of direct use only if
- you are doing cross-development of GEM software on these two systems. In
- this case, you will want to read this entire chapter, starting with the
- "What It Does" heading. You may also be interested in an article on
- PC/ST conversion written by Mark Skapinker of Batteries Included, which
- appears in the Fall 1986 issue of START magazine.
-
- If you are not doing cross machine development, you can still get
- something out of this column. Reading the program code and following the
- discussion, you will find practical examples of using GEM dialogs and
- following the internal structure of a resource file. Finally, there is a
- good deal of standard code for GEM initialization, AES utility
- functions, and GEMDOS file handling which you can extract for your own
- uses. If you fall in this category, skip the first parts of the column,
- and resume reading at the "How it Works" heading.
-
- The files and their contents for this chapter are:
-
- \begin{description}
-
- \item[RSCVMAIN.C ] This is the C source for main routine
- \item[RSCVFILE.C ] This is the C source of GEMDOS utilities
- \item[RSCVLIB.C ] C source of AES utility functions
- \item[RSCONV.PRG ] Linked binary of converter
- \item[RSCONV.RSC ] Binary resource image (ST format)
- \item[RSCONV.DFN ] Symbol file for resource (ST format for RCS 2.0)
- \item[RSCONV.H ] Object/tree name file for resource
- \item[RSCVLINK.SH ] C-shell script for linking RSCONV
-
- \end{description}
-
- The final two files will be of use only if you are using Dave
- Beckemeyer's Micro C-Shell environment. If not, you can simply translate
- them to \verb".BAT" and/or \verb".INP" form for use with the Atari batch program and
- linker.
-
-
- \subsection{What it does}
-
- A converter program is necessary because of the differing order of
- storage on the 68000 and Intel chips: the order of significance of bytes
- within words is reversed, as well as the order of words within long (32-
- bit) quantities. Attempting to load an unconverted resource on a GEM
- system with the other architecture will result in a crash, because all
- of the pointer and integer fields will be incorrect.
-
- In addition, the format of symbol definition (DEF) files differs between
- the PC and ST implementations of the RCS in its first version. In the
- latest version from DRI, the formats do correspond, and the files now
- carry an extent name of DFN. However, the byte swapping problem must be
- corrected in either format for the symbols to be correctly loaded.
-
- As final touch, the converter also checks to be sure that a resource
- being loaded from the PC has its bit images synchronized to an even byte
- boundary. If this is not corrected, the ST will suffer a bus error when
- attempted to address the images with a word type operation, which will
- certainly happen in either the RCS or the application itself on the
- target system.
-
-
- \subsection{Operation}
-
- When the Resource Converter loads, it presents an initial dialog box.
- (The converter does not use the menu bar.) The dialog has action buttons
- at the bottom labelled \verb"Help", \verb"Convert" and \verb"Quit". Clicking the Help
- button will obtain a screen which summarizes these operating
- instructions. The \verb"Quit" button terminates the program. Clicking \verb"Convert"
- initiates the program's action, according to the options you have set in
- the rest of the dialog.
-
- The top two buttons in the dialog establish the direction of the
- conversion. If you have built a resource on the PC and want to move it
- to the ST, click \verb"Intel->68000". If you are moving the file from the ST to
- the PC, click \verb"68000->Intel".
-
- The next two lines in the dialog establish the file extent names which
- will be used for the conversion. The input extent names are always on
- the left, no matter which conversion mode you selected above. By
- default, they are \verb"RSC" and \verb"DEF". The output extents, on the right, are RS2
- and \verb"DF2" by default. The \verb"RSC/RS2" file extents are used for the resource,
- and \verb"DEF/DF2" extents are for the symbol file. Be careful that the input
- files you use are actually of the type (Intel or 68000) which you
- specified. If you get it backwards, the ST will most likely crash,
- though the input files will not be harmed.
-
- The three buttons below the file extents determine the format of the
- input and output symbol files. If you click on \verb"DEF", the Resource
- Converter will assume that the input and output symbol files are in the
- old (that is, version 1.1) format used by the RCS. If you click on \verb"DFN",
- the program expects the new (version 2.0) symbol format. If the input
- symbol file extent is still set to \verb"DEF", clicking this button will also
- change it appropriately. (Note that the \verb"DEF" and \verb"DFN" formats are in fact
- IDENTICAL on the PC ONLY. To "change formats" ON THE PC, you need only
- change the file's extent name, for example, rename \verb"FOOBAZ.DEF" to
- \verb"FOOBAZ.DFN". On the ST, there is a real difference,and you must use the
- DRI \verb"DEF2DFN.PRG" utility to go from old to new.)
-
- The last button option is (OFF). Clicking this button disables the
- symbol file conversion. You may use this option to keep better control
- over versions of your resource. By establishing the main version on
- either the PC or ST, and converting only the resource file, not the
- symbols, you can ensure that no one will use the RCS on the destination
- machine to create a version which is different from the source.
-
- When the \verb"Convert" button is clicked to begin execution, the standard Item
- Selector is displayed. Use it to find and pick the input resource file
- which you want to convert. The Converter will use its main filename,
- substituting the various extents, to find the input definition file, and
- produce the output file names. If the definition file cannot be found in
- the same directory under the generated name, you will be presented with
- an alert, asking you to abort the conversion, continue without the
- symbol file, or specify a new name and/or directory for the file. If the
- output names coincide with existing files, they will be overwritten.
-
- While the conversion operation is occurring, a progress indicator box
- will be displayed on the screen, with text messages indicating the
- current phase of the operation. When the conversion is complete, you are
- returned to the initial dialog, where you may quit or perform another
- conversion.
-
- One last note on conversion, with a caution: it is possible to move
- files between Intel and 68000 even if you have two different versions of
- the Resource Construction Set, for instance, Version 2.0 on the PC and
- Version 1.1 on the ST. In this situation, you can take advantage of the
- identity between symbol file formats on the PC. Make a copy of the PC
- symbol file into another file with the \verb"DEF" extent, and run the
- converter. The output should load on the ST version correctly. However,
- if you move symbols between different RCS versions, you MUST NOT use the
- "free strings" and "free images" features of version 2 and then move the
- symbols to version 1. Doing so may result in spurious assignment of the
- "free" symbols to trees and objects, and you will (of course) not be
- able to edit the free images and/or strings in the resource.
-
-
- \subsection{How it works}
-
- For the ST-only developers now rejoining the discussion, I will now take
- a look at the standard GEM initialization code and the special dialog
- handling techniques which I have used in the Converter. Then we'll look
- at the guts of the code, which threads through the resource to do the
- actual conversion. For those interested in the supporting code
- libraries, the GEMDOS interface utilities in {\tt RSCVFILE.C} were described
- earlier. The progress indicator functions of {\tt RCSLIB.C} were
- detailed in the previous chapter, and {\tt map\_tree()} and standard dialog handling
- techniques were discussed in columns three through five.
-
- There are a number of useful facts in {\tt rscv\_init()}, the initialization
- code. First, notice that the result of \verb"appl_init" is NOT assigned
- directly to \verb"gl_apid". Due to a bug in the \verb"appl_init" binding the
- application ID is not returned as the function value. Instead it is
- returned to the global variable \verb"gl_apid". This bug wouldn't cause direct
- problems in the resource converter, but it will in any program which
- uses the ID when sending messages to its own pipe.
-
- Next, an alert string to be displayed if the resource is not found has
- been explicitly included, to avoid a "Catch-22" situation. You can
- easily create such a string by building the alert in the RCS, using the
- C output option, and extracting the string from the resulting file.
-
- {\tt Rscv\_init()} then sets up its VDI handle, by getting the physical
- workstation handle from \verb"graf_handle", and passing it to \verb"v_opnvwk" (open
- virtual workstation), which returns the virtual workstation handle for
- use in VDI calls. \verb"V_opnvwk" also returns the dimensions of the screen,
- which are copied to \verb"GRECT scrn_area", and the number of color planes,
- which is used to set up an \verb"MFDB" for the screen: \verb"scrn_mfdb".
-
- The \verb"wind_get" call for the working area of the DESK window (number zero)
- results in the usable \verb"GRECT" of the screen. This is different from
- \verb"scrn_area", because the menu bar's rectangle is reserved for the system.
-
- You may want to take particular notice of the \verb"vst_height" call, which
- reloads the character dimension variables. The values returned earlier
- by \verb"graf_handle" are ALWAYS for the monochrome (high resolution) system
- font, and they are not appropriate for the low and medium resolution
- modes of the ST. Performing the \verb"vst_height" call AFTER opening the
- virtual workstation will get the correct dimensions.
-
- Some of the environment variables which are set up by {\tt rscv\_init()}, are
- not actually used in the resource converter. I have included them
- because they are part of my "generic" initialization procedures.
-
- Finally, {\tt rscv\_init()} uses \verb"Malloc" to reserve most of memory as a working
- buffer for the resource conversion. Notice that 4K are left free. You
- must leave at least 2K, and preferably a safety factor, or the AES will
- be unable to allocate memory for the file selector dialog, and your
- application will hang when calling \verb"fsel_input". By the way, an open
- virtual workstation uses about 8K, so you should do your \verb"Mallocs" after
- the workstation call.
-
- We now turn our attention to {\tt do\_mode()}, which handles the main dialog of
- the converter. After getting the root address of the dialog tree, the
- states of global variables \verb"conv_def" and \verb"new_dfn" are used to set up the
- radio button array for symbol file conversion. \verb"Native_in" is used to set
- up the conversion type radio buttons. The last initialization step is to
- use the {\tt set\_text()} utility to link the file extent strings into the
- appropriate editable objects. Notice that this version of {\tt set\_text()}
- allows the string length to be explicitly supplied. For an editable text
- field object (\verb"G_FTEXT" or \verb"G_FBOXTEXT"), the length must be one greater
- than the number of blanks in the editing template.
-
- Since there are \verb"TOUCHEXIT" objects in the dialog, we cannot use the
- standard {\tt hndl\_dial()} routine. Instead, the form setup and draw calls are
- coded inline. The actual \verb"form_do" call is placed inside a loop. If the
- object returned is not one of the TOUCHEXIT objects, the loop is
- terminated.
-
- The two objects \verb"DEFYES" and \verb"DFNYES", which select the old and new symbol
- file formats, respectively, have been made \verb"TOUCHEXIT". If either is
- clicked, the radio button processing is done by the AES, but then
- control is returned from \verb"form_do". {\tt Do\_mode()} then tests the current input
- symbol file extent. If it is \verb"DEF" and we are switching from old to new
- mode, the extent is changed to \verb"DFN". When switching from new to old
- mode, the reverse check is made. In either case, the {\tt disp\_obj()} utility
- is used to force an immediate redraw before returning to \verb"form_do" for
- further user input.
-
- When the loop terminates, {\tt do\_mode()} cleans up the screen with \verb"form_dial",
- and uses the {\tt selected()} utility to retrieve the status of the radio
- button arrays and update the global variables. Finally, {\tt map\_tree()} is
- used to apply the deselection utility to all objects in the tree,
- leaving it clean for the next invocation of {\tt do\_mode()}.
-
-
- \subsection{The Belly of the Beast}
-
- The routine {\tt dconv()} contains the code for actually converting the
- resource. About half of {\tt do\_conv()} is devoted to accessing disk files,
- and correctly handling error conditions which might arise. The error
- recovery could perhaps be handled more concisely with artifices like
- \verb"setjmp", but I have left it in-line for the sake of clarity.
-
- {\tt Do\_conv()} also uses a collection of routines whose names begin with
- \verb"swap_" {\tt Swap\_bytes()} and {\tt swap\_words()} are the workhorse routines. They
- simply run through a given area of memory, reversing every pair of bytes
- or words, respectively. (Remember that the word swapper will only work
- on even byte boundaries, or a bus fault will occur.) The other swap
- routines do the fix up for one type of resource structure each. I'll
- take apart a couple in detail, so that you can see the similarities in
- the others.
-
- Now let's follow the {\tt do\_conv()} code. The first item of business is to
- use the {\tt get\_file()} utility, followed by {\tt open\_file()}, to find and open
- the input resource file. Again, refer to a previous chapter for a discussion
- of these routines, which will handle DOS errors if they occur.
-
- The Boolean \verb"conv_def" is \verb"TRUE" if we need to open a symbols file. If
- everything goes right, just substituting a new extent name (from
- variable \verb"old_def"), and doing the file open will get the file. If things
- foul up, {\tt do\_conv()} has to recover gracefully. So, it puts up the three
- button alert \verb"NODEF". The third button is an abort option; if it's picked
- we punt, closing the one open file and returning to the main dialog.
- Button one allows a continuation without the definition file (\verb"conv_def"
- is forced \verb"FALSE" so the user won't make the same mistake twice). Button
- two says try again, so {\tt get\_file()} is called to pick another file, and
- the whole process repeats.
-
- Once the input file(s) are open, the operation will run to completion,
- assuming no disk errors. The mouse form is switched to an hour glass,
- and a progress indicator box is initialized on the screen.
-
- The first action is to bring in the resource header only. If the
- resource is not native, that is, is not in 68000 format, its bytes must
- be swapped before using any of the counts and offsets. Note: if you've
- never dissected a resource before, this would be a good time take a look
- at the header format given in {\tt gemdefs.h} in the Developer's Kit, and
- the resource structure definitions in {\tt obdefs.h}.
-
- After the swap, if necessary, the resource size field can be used.
- Before reading the rest of the resource, the size must be compared
- against the size of the buffer allocated during initialization. If there
- isn't enough room, the \verb"NOMEM" alert is displayed and control returns to
- the main dialog. (Since the biggest resource is 64K, and the Converter
- is rather small, this shouldn't ever happen. But, some people load up
- lots of desk accessories, so best to be prepared.)
-
- After completing the resource input, an (admittedly kludgy) patch is
- made for the odd-byte-images problem. If the image offset pointer is
- odd, there will be problems on the ST. The fix made here relies on two
- facts. First, all images are an even number of bytes in size. Second,
- the old version of RCS DOES do an even byte synchronization AFTER
- writing the images. Therefore, if the images start on an odd byte, they
- will end on an even byte, and have one unused odd-addressed byte
- immediately following. The fixup strategy is to move all of the images
- up one byte, patch the offset in the header accordingly, and leave the
- \verb"img_odd" flag set so that the bit image swap routine will also increment
- pointers in the \verb"BITBLK" and \verb"ICONBLK"s which use the images.
-
- If the resource being converted is native, we can do the image fixup
- immediately. If not, all of the other swaps have to take first, because
- the long pointers in the \verb"BITBLK"s and \verb"ICONBLK"s are in the wrong order.
- (We don't have to swap string data; it's always stored in ascending
- order.)
-
- {\tt Do\_conv()} calls subroutines to swap the tree index, objects, \verb"TEDINFO"s,
- \verb"ICONBLK"s, \verb"BITBLK"s, and free string and images, in that order. We'll look
- at their actual code later. When this is finished, one of two cleanup
- actions is needed. If the resource WAS native, everything is now swapped
- except the header, which is finally reversed. If it used to be foreign
- (Intel), the pointers are now in proper 68000 order, so {\tt swap\_images()}
- can be called successfully.
-
- Now it's time to write the converted resource, taking care that no error
- results. If no symbol file conversion is needed, {\tt do\_conv()} is done. The
- files are closed, the progress indicator completed, and control returns
- to {\tt rscv\_run()}, which will set the cursor back to the arrow. Otherwise,
- it's time to handle the symbols file.
-
- The first two bytes in the definition file are always the symbol count.
- They are read into \verb"nsym", and a scratch copy is made in reply, and
- swapped. Just which version is written to the output file depends on
- whether it is a \verb"DFN" or \verb"DEF" file. The new (\verb"DFN")
- format keeps a similar
- word order on both the ST and PC. For these files, the following code
- amounts to a verbatim copy.
-
- Nsym is now used as the control variable of a loop which reads,
- converts, and writes one symbol entry on each iteration. In the old
- (\verb"DEF") file format, there was an extra word in the value field of each
- symbol's entry on the ST. Going from PC to ST, two padding zero bytes
- are added. Going the other way, they are read and discarded. For the old
- format, both the significant word of this field, as well as the
- following word field (the type) are swapped. The ten symbol bytes are
- passed through verbatim in both new and old format.
-
- After swapping (old format) or simply copying (new format) of the symbol
- file is completed, file and progress box cleanup is done, and control
- returns.
-
-
- \subsection{The ol' switcheroo}
-
- As promised, let's now go and look at a sampling of the structure swap
- routines: one of the simplest (tree index), one of medium complexity
- (objects), and the worst of the bunch (bit images).
-
- {\tt Swap\_trees()} is responsible for fixing the tree index. Its location is
- found by adding an offset from the header (\verb"rsh_trindex") to the base
- address of the resource, stored in head. (Notice that head was defined
- as a byte pointer when the buffer was allocated, so I need to type
- coerce it to a resource header pointer before making structure
- references.) The number of trees is stored in the \verb"rsh_ntree" header
- field. Each index entry is a long pointer of 32-bits, so the total size
- of the index may be found by multiplication. Finally, the swapping is
- done, first bytes within words, then words within the long pointers.
- (The order doesn't matter - work it out!)
-
- {\tt Swap\_objs()} takes care of swapping fields inside of object structures.
- It begins similarly to {\tt swap\_trees()}, computing the base address of the
- objects from \verb"rsh_object", and taking the count from \verb"rsh_nobs". Since all
- of the objects are contiguous in a resource produced by RCS, and all
- fields of an object are words or longs, a byte swap is performed on the
- entire block at once. Now there are a couple of tricks. Notice that
- \verb"where" in this routine is typed as an object pointer. As the number of
- objects is counted down in the loop, \verb"where" is incremented. Because of
- its type, its actual pointer value will increase by \verb"sizeof(OBJECT)" at
- each iteration. Inside the loop, only one long field, the \verb"ob_spec", needs
- to be word swapped. When the loop completes, all object structures are
- fixed.
-
- {\tt Swap\_fstr()} and {\tt swap\_fimg()} use code similar to {\tt swap\_trees()}. The other
- resource structures, \verb"TEDINFO"s, \verb"ICONBLK"s, and \verb"BITBLK"s, are transformed by
- code similar to {\tt swap\_objs()}. There is one subtlety here. These
- structures would normally be reaching by following an object's \verb"ob_spec"
- pointer. To finesse a "swap now or later" ordering problem with the
- objects, I have instead used the resource header to find the structure
- arrays. You SHOULD NOT use this technique at run time, for two reasons.
- First, if you do any object patching on the fly, the correspondence
- between objects and reference strucutres will be destroyed. Second,
- there is no way to name a non-object structure with the RCS, and
- therefore no way to reliably retrieve it with \verb"rsrc_gaddr" at execution
- time.
-
- Last but not least, {\tt swap\_images()} not only takes care of switching bit
- image words, but also fixes image pointers as necessary if an odd byte
- problem has been detected. For these reasons, {\tt swap\_images} DOES follow
- pointers from the \verb"ICONBLK"s and \verb"BITBLK"s. The loop structure of the two
- sections of code will be familiar from {\tt swap\_ibs()} and {\tt swap\_bbs()}. Size
- fields within the structure are used to determine the length of each
- image. If the \verb"img_odd" flag has been set, the data pointer must be
- incremented BEFORE doing the swap, since the underlying data has already
- been moved.
-
- \newpage
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \begin{appendix}
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \section{Commonly asked Questions}
-
- Q: {\bf The ST RCS does not seem to support multiple screen resolutions
- properly. The spacing between boxes and text is not right. Is this a
- buggy RCS?}
-
- A: Yes, there is a known bug in the RCS with color systems. It is
- (incorrectly) trying to use the character size for monochrome mode,
- which has twice as many vertical pixels. Digital Research is working on
- a fixed version, but for now you must either use a a monochrome system
- with RCS, or do some patches by hand.
-
-
- Q: {\bf I have created a menu bar using the RCS, and now I need to display it
- using C. How do I go about it?}
-
- A: First, you must find the address of the menu tree using:
-
- \begin{verbatim}
- rsrc_gaddr(R_TREE, MYMENU, &ad_menu);
- \end{verbatim}
-
- This assumes that MYMENU is the name you gave your menu in the RCS, and
- that \verb"ad_menu" is a LONG variable. Then you make the call:
-
- \begin{verbatim}
- menu_bar(ad_menu, TRUE);
- \end{verbatim}
-
- which will cause GEM to draw the menu and begin sending menu messages to
- your application. When your program is done, remove the menu bar with:
-
- \begin{verbatim}
- menu_bar(0x0L, FALSE);
- \end{verbatim}
-
- before terminating.
-
-
- Q: {\bf What is the WM\_NEWTOP message? How is it different from WM\_TOPPED?}
-
- A: WM\_NEWTOP is a message which is sent to an application when one of
- its windows is about to be REMOVED from the top position. Most programs
- may safely ignore this message. It is meant for use by "Paint-like"
- programs which use a window as an active buffer area and must be able to
- save away its contents before they are overlaid by a desk accessory or
- another window. WM\_NEWTOP contrasts with WM\_TOPPED, which is sent when
- the window is about to be PLACED in the top position.
-
-
- Q: {\bf In RCS, what is the format of the {\tt .ICN} files which can
- be used with the LOAD option for icons and bit images?}
-
- A: The RCS expects the {\tt .ICN} files to be in an obscure ASCII hexadecimal
- format which is produced by the GEM IconEditor. Unfortunately, the
- IconEditor has not yet been ported to the ST. So that you can get an
- idea of this format, I have included the images of two {\tt .ICN} files within
- the listings. You might try to extract the text and load them into RCS
- as the data and mask for an icon. If you do it right, you should see a
- clock face.
-
- If you want to build your own {\tt .ICN} files, you must keep the rigid four
- number per line format while adding and removing data lines. You may
- change the vertical and horizontal pixel counts, but the horizontal
- count must always be a multiple of sixteen. Finally, the RCS expects the
- images to be in device-independent, monochrome format.
-
-
- Q: {\bf The AES manual states "when the application is first loaded into
- memory, it should make a DOS call to modify the application's memory
- allocation". How is this done?}
-
- A: When a GEM application is first loaded by the AES, all of memory is
- allocated to it. It must then release that portion which is past the
- end of its field length. This is necessary since the AES file selector
- and the VDI open workstation and load fonts calls allocate memory, and
- the application itself may need to do its own memory management.
-
- Fortunately, the standard routine APSTART will release memory for you if
- it is included as the first item in your link. It uses a setblock call
- to TOS, after determining the proper length of your application. To do
- so (quoting from APSTART), all "segment" lengths in the base page are
- totaled and 0x100 is added for the base page length.
-
-
- Q: {\bf Is it possible to patch GEM so that you must first click on the menu
- bar to active a drop-down, rather than just touching it with the cursor?
- I find it mildly irritating to open a drop-down menu accidently when I
- am merely moving the cursor around.}
-
- A: As you may have guessed from the preceding discussion, the GEM menu
- algorithm is embedded deeply in the AES code and is not patchable. The
- "drop-down" architecture was chosen over "pull-downs" because it proved
- easier for novices. Unfortunately, it does cause problems for
- experienced users who mouse around the screen much faster.
-
- One way to partially avoid the problem is to design applications which
- have some separation space between the menu bar and selectable objects.
- On the Desktop, you can move file windows away from the bar and then
- save the new layout.
-
-
- Q: {\bf How can I support multiple resolutions if I need to include icons and
- images in my resource? Do I need to have an entire separate resource
- for each resolution, or is there a simpler way?}
-
- A: It is certainly possible to have an alternate resource for each
- screen mode, but it introduces problems in keeping their structure and
- naming identical. Instead, you might consider building separate files
- containing only the bit image data for your resource, with one version
- for each resolution. You might do this by copying the image definitions
- from the {\tt .C} file emitted by the RCS.
-
- After you have loaded the resource, allocate some memory and read in
- the appropriate image file. You will then have to link in the image
- data by modifying the pointers in your resource's BITBLK and ICONBLK
- structures. You will need to determine in advance which objects must be
- modified, and in what order their data occurs in the image file.
-
-
- Q: {\bf Why are there separate startup procedures for applications and desk
- accessories ({\tt APSTART.S} and {\tt ACCSTART.S})?}
-
- A: A desk accessory is started up automatically when GEM is booted, and
- becomes more or less part of the system. An application is only run at
- the user's request, after the system has finished loading. Since they
- start at different times, they have a different "environment" to contend
- with. For instance, the application startup code has to release memory
- following the program, while the accessory has this done for it by GEM.
-
-
- Q: {\bf Can a desk accessory use a resource?}
-
- A: In general, no. This is because the accessories are loaded at system
- initialization time. If one later makes a {\tt rsrc\_load()} call for its
- resource, the system would allocate the resource's buffer in the user
- area of memory. This would result in a memory allocation error when the
- next application attempted to run.
-
- However, it is still possible to use AES object trees within an
- accessory. You should use the RCS to create the trees, then use the C
- output option to create an image of them. This image may be included and
- compiled in the accessory's source code. At run time, you will have to
- adjust the size of the objects using {\tt rsrc\_obfix()}, as well as fix up the
- inter-structure pointers. You can use the source code of {\tt STCREATE.C} and
- the discussion of resource structure in a prior column as guides.
-
-
- Q: {\bf Are there problems with the {\tt--f} (floating point)
- option in Alcyon C?}
-
- A: Yes, there are a number of bugs in the Alcyon floating point library.
- In many cases, the precision of operations is poor. The only solution is
- to switch to Megamax or Lattice C to develop your floating point
- application.
-
-
- Q: {\bf I've noticed that the HEAD, TAIL, and NEXT pointers in an object tree
- are actually numbers relative to the base of the tree. Does this mean
- that an object must be pre-allocated within the tree if I plan to
- {\tt objc\_add()} it later? Can an application do true dynamic allocation of
- objects without setting aside space in the tree?}
-
- A: Yes, you must preallocate objects if you want to add them without
- moving the tree from the resource. The alternative is to create a
- working area from your program and copy the tree to be altered to this
- buffer. Then you can allocate new objects following those which exist,
- and link them in. The new objects must be created on 24-byte boundaries
- starting at the root of the tree. You do NOT need to copy over
- structures such as TEDINFOs, unless you plan to modify them also.
-
-
- Q: {\bf I'm using the Atari Developer's Kit, and LINK68 seems to crash when
- my programs get large. Is this a bug?}
-
- A: Yes, there is a known bug in using Alcyon C and LINK68 when a
- program's code area approaches 64K. It appears that jump tables
- generated by SWITCH and GOTO statements are not being properly
- relocated. Often the difficulty can be avoided by putting such modules
- at the start of the link. Otherwise, you will need to to recode SWITCH
- statements as IFs, and eliminate the GOTOs (a good practice anyway).
-
- %~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- \section{Listings of example source files}
-
- Your disk should contain the following example source files:
-
- \begin{verbatim}
- \
- GEMCL02.C
- GEMCL03.C
- GEMCL04.C
- GEMCL05.C
- GEMCL06.C
- GEMCL07.C
- GEMCL09.C
- GEMCL10.C
- GEMCL11.C
- GEMCL13.C
- GEMCL15.C
- GEMCL16\
- GMCL16.C
- GMCL16.H
- GMCL16.RSC
- GMCL16.RSH
- GMCL16.DFN
-
- GEMCL17\
- RSCVFILE.C
- RSCVLIB.C
- RSCVMAIN.C
- RSCONV.H
- RSCVLINK.SH
- RSCONV.PRG
- RSCONV.RSC
- RSCONV.DFN
-
- \end{verbatim}
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- % \newpage
- % \include{app_a}
- % \include{app_b}
- % \include{app_c}
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \end{appendix}
-
- % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- \end{document}
-